| # window.fetch polyfill |
| |
| The `fetch()` function is a Promise-based mechanism for programmatically making |
| web requests in the browser. This project is a polyfill that implements a subset |
| of the standard [Fetch specification][], enough to make `fetch` a viable |
| replacement for most uses of XMLHttpRequest in traditional web applications. |
| |
| ## Table of Contents |
| |
| * [Read this first](#read-this-first) |
| * [Installation](#installation) |
| * [Usage](#usage) |
| * [Importing](#importing) |
| * [HTML](#html) |
| * [JSON](#json) |
| * [Response metadata](#response-metadata) |
| * [Post form](#post-form) |
| * [Post JSON](#post-json) |
| * [File upload](#file-upload) |
| * [Caveats](#caveats) |
| * [Handling HTTP error statuses](#handling-http-error-statuses) |
| * [Sending cookies](#sending-cookies) |
| * [Receiving cookies](#receiving-cookies) |
| * [Obtaining the Response URL](#obtaining-the-response-url) |
| * [Aborting requests](#aborting-requests) |
| * [Browser Support](#browser-support) |
| |
| ## Read this first |
| |
| * If you believe you found a bug with how `fetch` behaves in your browser, |
| please **don't open an issue in this repository** unless you are testing in |
| an old version of a browser that doesn't support `window.fetch` natively. |
| This project is a _polyfill_, and since all modern browsers now implement the |
| `fetch` function natively, **no code from this project** actually takes any |
| effect there. See [Browser support](#browser-support) for detailed |
| information. |
| |
| * If you have trouble **making a request to another domain** (a different |
| subdomain or port number also constitutes another domain), please familiarize |
| yourself with all the intricacies and limitations of [CORS][] requests. |
| Because CORS requires participation of the server by implementing specific |
| HTTP response headers, it is often nontrivial to set up or debug. CORS is |
| exclusively handled by the browser's internal mechanisms which this polyfill |
| cannot influence. |
| |
| * This project **doesn't work under Node.js environments**. It's meant for web |
| browsers only. You should ensure that your application doesn't try to package |
| and run this on the server. |
| |
| * If you have an idea for a new feature of `fetch`, **submit your feature |
| requests** to the [specification's repository](https://github.com/whatwg/fetch/issues). |
| We only add features and APIs that are part of the [Fetch specification][]. |
| |
| ## Installation |
| |
| ``` |
| npm install whatwg-fetch --save |
| ``` |
| |
| You will also need a Promise polyfill for [older browsers](http://caniuse.com/#feat=promises). |
| We recommend [taylorhakes/promise-polyfill](https://github.com/taylorhakes/promise-polyfill) |
| for its small size and Promises/A+ compatibility. |
| |
| ## Usage |
| |
| For a more comprehensive API reference that this polyfill supports, refer to |
| https://github.github.io/fetch/. |
| |
| ### Importing |
| |
| Importing will automatically polyfill `window.fetch` and related APIs: |
| |
| ```javascript |
| import 'whatwg-fetch' |
| |
| window.fetch(...) |
| ``` |
| |
| If for some reason you need to access the polyfill implementation, it is |
| available via exports: |
| |
| ```javascript |
| import {fetch as fetchPolyfill} from 'whatwg-fetch' |
| |
| window.fetch(...) // use native browser version |
| fetchPolyfill(...) // use polyfill implementation |
| ``` |
| |
| This approach can be used to, for example, use [abort |
| functionality](#aborting-requests) in browsers that implement a native but |
| outdated version of fetch that doesn't support aborting. |
| |
| For use with webpack, add this package in the `entry` configuration option |
| before your application entry point: |
| |
| ```javascript |
| entry: ['whatwg-fetch', ...] |
| ``` |
| |
| ### HTML |
| |
| ```javascript |
| fetch('/users.html') |
| .then(function(response) { |
| return response.text() |
| }).then(function(body) { |
| document.body.innerHTML = body |
| }) |
| ``` |
| |
| ### JSON |
| |
| ```javascript |
| fetch('/users.json') |
| .then(function(response) { |
| return response.json() |
| }).then(function(json) { |
| console.log('parsed json', json) |
| }).catch(function(ex) { |
| console.log('parsing failed', ex) |
| }) |
| ``` |
| |
| ### Response metadata |
| |
| ```javascript |
| fetch('/users.json').then(function(response) { |
| console.log(response.headers.get('Content-Type')) |
| console.log(response.headers.get('Date')) |
| console.log(response.status) |
| console.log(response.statusText) |
| }) |
| ``` |
| |
| ### Post form |
| |
| ```javascript |
| var form = document.querySelector('form') |
| |
| fetch('/users', { |
| method: 'POST', |
| body: new FormData(form) |
| }) |
| ``` |
| |
| ### Post JSON |
| |
| ```javascript |
| fetch('/users', { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json' |
| }, |
| body: JSON.stringify({ |
| name: 'Hubot', |
| login: 'hubot', |
| }) |
| }) |
| ``` |
| |
| ### File upload |
| |
| ```javascript |
| var input = document.querySelector('input[type="file"]') |
| |
| var data = new FormData() |
| data.append('file', input.files[0]) |
| data.append('user', 'hubot') |
| |
| fetch('/avatars', { |
| method: 'POST', |
| body: data |
| }) |
| ``` |
| |
| ### Caveats |
| |
| * The Promise returned from `fetch()` **won't reject on HTTP error status** |
| even if the response is an HTTP 404 or 500. Instead, it will resolve normally, |
| and it will only reject on network failure or if anything prevented the |
| request from completing. |
| |
| * For maximum browser compatibility when it comes to sending & receiving |
| cookies, always supply the `credentials: 'same-origin'` option instead of |
| relying on the default. See [Sending cookies](#sending-cookies). |
| |
| #### Handling HTTP error statuses |
| |
| To have `fetch` Promise reject on HTTP error statuses, i.e. on any non-2xx |
| status, define a custom response handler: |
| |
| ```javascript |
| function checkStatus(response) { |
| if (response.status >= 200 && response.status < 300) { |
| return response |
| } else { |
| var error = new Error(response.statusText) |
| error.response = response |
| throw error |
| } |
| } |
| |
| function parseJSON(response) { |
| return response.json() |
| } |
| |
| fetch('/users') |
| .then(checkStatus) |
| .then(parseJSON) |
| .then(function(data) { |
| console.log('request succeeded with JSON response', data) |
| }).catch(function(error) { |
| console.log('request failed', error) |
| }) |
| ``` |
| |
| #### Sending cookies |
| |
| For [CORS][] requests, use `credentials: 'include'` to allow sending credentials |
| to other domains: |
| |
| ```javascript |
| fetch('https://example.com:1234/users', { |
| credentials: 'include' |
| }) |
| ``` |
| |
| To disable sending or receiving cookies for requests to any domain, including |
| the current one, use the "omit" value: |
| |
| ```javascript |
| fetch('/users', { |
| credentials: 'omit' |
| }) |
| ``` |
| |
| The default value for `credentials` is "same-origin". |
| |
| The default for `credentials` wasn't always the same, though. The following |
| versions of browsers implemented an older version of the fetch specification |
| where the default was "omit": |
| |
| * Firefox 39-60 |
| * Chrome 42-67 |
| * Safari 10.1-11.1.2 |
| |
| If you target these browsers, it's advisable to always specify `credentials: |
| 'same-origin'` explicitly with all fetch requests instead of relying on the |
| default: |
| |
| ```javascript |
| fetch('/users', { |
| credentials: 'same-origin' |
| }) |
| ``` |
| |
| #### Receiving cookies |
| |
| As with XMLHttpRequest, the `Set-Cookie` response header returned from the |
| server is a [forbidden header name][] and therefore can't be programmatically |
| read with `response.headers.get()`. Instead, it's the browser's responsibility |
| to handle new cookies being set (if applicable to the current URL). Unless they |
| are HTTP-only, new cookies will be available through `document.cookie`. |
| |
| #### Obtaining the Response URL |
| |
| Due to limitations of XMLHttpRequest, the `response.url` value might not be |
| reliable after HTTP redirects on older browsers. |
| |
| The solution is to configure the server to set the response HTTP header |
| `X-Request-URL` to the current URL after any redirect that might have happened. |
| It should be safe to set it unconditionally. |
| |
| ``` ruby |
| # Ruby on Rails controller example |
| response.headers['X-Request-URL'] = request.url |
| ``` |
| |
| This server workaround is necessary if you need reliable `response.url` in |
| Firefox < 32, Chrome < 37, Safari, or IE. |
| |
| #### Aborting requests |
| |
| This polyfill supports |
| [the abortable fetch API](https://developers.google.com/web/updates/2017/09/abortable-fetch). |
| However, aborting a fetch requires use of two additional DOM APIs: |
| [AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) and |
| [AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal). |
| Typically, browsers that do not support fetch will also not support |
| AbortController or AbortSignal. Consequently, you will need to include |
| [an additional polyfill](https://github.com/mo/abortcontroller-polyfill#readme) |
| for these APIs to abort fetches: |
| |
| ```js |
| import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only' |
| import {fetch} from 'whatwg-fetch' |
| |
| // use native browser implementation if it supports aborting |
| const abortableFetch = ('signal' in new Request('')) ? window.fetch : fetch |
| |
| const controller = new AbortController() |
| |
| abortableFetch('/avatars', { |
| signal: controller.signal |
| }).catch(function(ex) { |
| if (ex.name === 'AbortError') { |
| console.log('request aborted') |
| } |
| }) |
| |
| // some time later... |
| controller.abort() |
| ``` |
| |
| ## Browser Support |
| |
| - Chrome |
| - Firefox |
| - Safari 6.1+ |
| - Internet Explorer 10+ |
| |
| Note: modern browsers such as Chrome, Firefox, Microsoft Edge, and Safari contain native |
| implementations of `window.fetch`, therefore the code from this polyfill doesn't |
| have any effect on those browsers. If you believe you've encountered an error |
| with how `window.fetch` is implemented in any of these browsers, you should file |
| an issue with that browser vendor instead of this project. |
| |
| |
| [fetch specification]: https://fetch.spec.whatwg.org |
| [cors]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS |
| "Cross-origin resource sharing" |
| [csrf]: https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet |
| "Cross-site request forgery" |
| [forbidden header name]: https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_header_name |