UNPKG

10.8 kBMarkdownView Raw
1# window.fetch polyfill
2
3The `fetch()` function is a Promise-based mechanism for programmatically making
4web requests in the browser. This project is a polyfill that implements a subset
5of the standard [Fetch specification][], enough to make `fetch` a viable
6replacement for most uses of XMLHttpRequest in traditional web applications.
7
8## Table of Contents
9
10* [Read this first](#read-this-first)
11* [Installation](#installation)
12* [Usage](#usage)
13 * [Importing](#importing)
14 * [HTML](#html)
15 * [JSON](#json)
16 * [Response metadata](#response-metadata)
17 * [Post form](#post-form)
18 * [Post JSON](#post-json)
19 * [File upload](#file-upload)
20 * [Caveats](#caveats)
21 * [Handling HTTP error statuses](#handling-http-error-statuses)
22 * [Sending cookies](#sending-cookies)
23 * [Receiving cookies](#receiving-cookies)
24 * [Redirect modes](#redirect-modes)
25 * [Obtaining the Response URL](#obtaining-the-response-url)
26 * [Aborting requests](#aborting-requests)
27* [Browser Support](#browser-support)
28
29## Read this first
30
31* If you believe you found a bug with how `fetch` behaves in your browser,
32 please **don't open an issue in this repository** unless you are testing in
33 an old version of a browser that doesn't support `window.fetch` natively.
34 This project is a _polyfill_, and since all modern browsers now implement the
35 `fetch` function natively, **no code from this project** actually takes any
36 effect there. See [Browser support](#browser-support) for detailed
37 information.
38
39* If you have trouble **making a request to another domain** (a different
40 subdomain or port number also constitutes another domain), please familiarize
41 yourself with all the intricacies and limitations of [CORS][] requests.
42 Because CORS requires participation of the server by implementing specific
43 HTTP response headers, it is often nontrivial to set up or debug. CORS is
44 exclusively handled by the browser's internal mechanisms which this polyfill
45 cannot influence.
46
47* This project **doesn't work under Node.js environments**. It's meant for web
48 browsers only. You should ensure that your application doesn't try to package
49 and run this on the server.
50
51* If you have an idea for a new feature of `fetch`, **submit your feature
52 requests** to the [specification's repository](https://github.com/whatwg/fetch/issues).
53 We only add features and APIs that are part of the [Fetch specification][].
54
55## Installation
56
57```
58npm install whatwg-fetch --save
59```
60
61As an alternative to using npm, you can obtain `fetch.umd.js` from the
62[Releases][] section. The UMD distribution is compatible with AMD and CommonJS
63module loaders, as well as loading directly into a page via `<script>` tag.
64
65You will also need a Promise polyfill for [older browsers](http://caniuse.com/#feat=promises).
66We recommend [taylorhakes/promise-polyfill](https://github.com/taylorhakes/promise-polyfill)
67for its small size and Promises/A+ compatibility.
68
69## Usage
70
71For a more comprehensive API reference that this polyfill supports, refer to
72https://github.github.io/fetch/.
73
74### Importing
75
76Importing will automatically polyfill `window.fetch` and related APIs:
77
78```javascript
79import 'whatwg-fetch'
80
81window.fetch(...)
82```
83
84If for some reason you need to access the polyfill implementation, it is
85available via exports:
86
87```javascript
88import {fetch as fetchPolyfill} from 'whatwg-fetch'
89
90window.fetch(...) // use native browser version
91fetchPolyfill(...) // use polyfill implementation
92```
93
94This approach can be used to, for example, use [abort
95functionality](#aborting-requests) in browsers that implement a native but
96outdated version of fetch that doesn't support aborting.
97
98For use with webpack, add this package in the `entry` configuration option
99before your application entry point:
100
101```javascript
102entry: ['whatwg-fetch', ...]
103```
104
105### HTML
106
107```javascript
108fetch('/users.html')
109 .then(function(response) {
110 return response.text()
111 }).then(function(body) {
112 document.body.innerHTML = body
113 })
114```
115
116### JSON
117
118```javascript
119fetch('/users.json')
120 .then(function(response) {
121 return response.json()
122 }).then(function(json) {
123 console.log('parsed json', json)
124 }).catch(function(ex) {
125 console.log('parsing failed', ex)
126 })
127```
128
129### Response metadata
130
131```javascript
132fetch('/users.json').then(function(response) {
133 console.log(response.headers.get('Content-Type'))
134 console.log(response.headers.get('Date'))
135 console.log(response.status)
136 console.log(response.statusText)
137})
138```
139
140### Post form
141
142```javascript
143var form = document.querySelector('form')
144
145fetch('/users', {
146 method: 'POST',
147 body: new FormData(form)
148})
149```
150
151### Post JSON
152
153```javascript
154fetch('/users', {
155 method: 'POST',
156 headers: {
157 'Content-Type': 'application/json'
158 },
159 body: JSON.stringify({
160 name: 'Hubot',
161 login: 'hubot',
162 })
163})
164```
165
166### File upload
167
168```javascript
169var input = document.querySelector('input[type="file"]')
170
171var data = new FormData()
172data.append('file', input.files[0])
173data.append('user', 'hubot')
174
175fetch('/avatars', {
176 method: 'POST',
177 body: data
178})
179```
180
181### Caveats
182
183* The Promise returned from `fetch()` **won't reject on HTTP error status**
184 even if the response is an HTTP 404 or 500. Instead, it will resolve normally,
185 and it will only reject on network failure or if anything prevented the
186 request from completing.
187
188* For maximum browser compatibility when it comes to sending & receiving
189 cookies, always supply the `credentials: 'same-origin'` option instead of
190 relying on the default. See [Sending cookies](#sending-cookies).
191
192* Not all Fetch standard options are supported in this polyfill. For instance,
193 [`redirect`](#redirect-modes) and
194 [`cache`](https://github.github.io/fetch/#caveats) directives are ignored.
195
196#### Handling HTTP error statuses
197
198To have `fetch` Promise reject on HTTP error statuses, i.e. on any non-2xx
199status, define a custom response handler:
200
201```javascript
202function checkStatus(response) {
203 if (response.status >= 200 && response.status < 300) {
204 return response
205 } else {
206 var error = new Error(response.statusText)
207 error.response = response
208 throw error
209 }
210}
211
212function parseJSON(response) {
213 return response.json()
214}
215
216fetch('/users')
217 .then(checkStatus)
218 .then(parseJSON)
219 .then(function(data) {
220 console.log('request succeeded with JSON response', data)
221 }).catch(function(error) {
222 console.log('request failed', error)
223 })
224```
225
226#### Sending cookies
227
228For [CORS][] requests, use `credentials: 'include'` to allow sending credentials
229to other domains:
230
231```javascript
232fetch('https://example.com:1234/users', {
233 credentials: 'include'
234})
235```
236
237The default value for `credentials` is "same-origin".
238
239The default for `credentials` wasn't always the same, though. The following
240versions of browsers implemented an older version of the fetch specification
241where the default was "omit":
242
243* Firefox 39-60
244* Chrome 42-67
245* Safari 10.1-11.1.2
246
247If you target these browsers, it's advisable to always specify `credentials:
248'same-origin'` explicitly with all fetch requests instead of relying on the
249default:
250
251```javascript
252fetch('/users', {
253 credentials: 'same-origin'
254})
255```
256
257Note: due to [limitations of
258XMLHttpRequest](https://github.com/github/fetch/pull/56#issuecomment-68835992),
259using `credentials: 'omit'` is not respected for same domains in browsers where
260this polyfill is active. Cookies will always be sent to same domains in older
261browsers.
262
263#### Receiving cookies
264
265As with XMLHttpRequest, the `Set-Cookie` response header returned from the
266server is a [forbidden header name][] and therefore can't be programmatically
267read with `response.headers.get()`. Instead, it's the browser's responsibility
268to handle new cookies being set (if applicable to the current URL). Unless they
269are HTTP-only, new cookies will be available through `document.cookie`.
270
271#### Redirect modes
272
273The Fetch specification defines these values for [the `redirect`
274option](https://fetch.spec.whatwg.org/#concept-request-redirect-mode): "follow"
275(the default), "error", and "manual".
276
277Due to limitations of XMLHttpRequest, only the "follow" mode is available in
278browsers where this polyfill is active.
279
280#### Obtaining the Response URL
281
282Due to limitations of XMLHttpRequest, the `response.url` value might not be
283reliable after HTTP redirects on older browsers.
284
285The solution is to configure the server to set the response HTTP header
286`X-Request-URL` to the current URL after any redirect that might have happened.
287It should be safe to set it unconditionally.
288
289``` ruby
290# Ruby on Rails controller example
291response.headers['X-Request-URL'] = request.url
292```
293
294This server workaround is necessary if you need reliable `response.url` in
295Firefox < 32, Chrome < 37, Safari, or IE.
296
297#### Aborting requests
298
299This polyfill supports
300[the abortable fetch API](https://developers.google.com/web/updates/2017/09/abortable-fetch).
301However, aborting a fetch requires use of two additional DOM APIs:
302[AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController) and
303[AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal).
304Typically, browsers that do not support fetch will also not support
305AbortController or AbortSignal. Consequently, you will need to include
306[an additional polyfill](https://github.com/mo/abortcontroller-polyfill#readme)
307for these APIs to abort fetches:
308
309```js
310import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only'
311import {fetch} from 'whatwg-fetch'
312
313// use native browser implementation if it supports aborting
314const abortableFetch = ('signal' in new Request('')) ? window.fetch : fetch
315
316const controller = new AbortController()
317
318abortableFetch('/avatars', {
319 signal: controller.signal
320}).catch(function(ex) {
321 if (ex.name === 'AbortError') {
322 console.log('request aborted')
323 }
324})
325
326// some time later...
327controller.abort()
328```
329
330## Browser Support
331
332- Chrome
333- Firefox
334- Safari 6.1+
335- Internet Explorer 10+
336
337Note: modern browsers such as Chrome, Firefox, Microsoft Edge, and Safari contain native
338implementations of `window.fetch`, therefore the code from this polyfill doesn't
339have any effect on those browsers. If you believe you've encountered an error
340with how `window.fetch` is implemented in any of these browsers, you should file
341an issue with that browser vendor instead of this project.
342
343
344 [fetch specification]: https://fetch.spec.whatwg.org
345 [cors]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
346 "Cross-origin resource sharing"
347 [csrf]: https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet
348 "Cross-site request forgery"
349 [forbidden header name]: https://developer.mozilla.org/en-US/docs/Glossary/Forbidden_header_name
350 [releases]: https://github.com/github/fetch/releases
351
\No newline at end of file