The Javascript Fetch API is the current standard to make asynchronous HTTP Requests. To anyone who used XMLHttpRequest and Jquery.ajax(), this new API provides more powerful and flexible features.
The main features of the Javascript Fetch API are:
- Generic definition of
Request
andResponse
objects- More control over request properties and content
- More details about response properties and content.
- Interaction with other Web APIs (for example: Cache API, Blob API)
- Better control and setup for CORS-related requests
- Implemented for Web pages as
window.fetch()
and for Web workers withWorkerGlobalScope.fetch()
What’s different
Implementation of XMLHttpRequest
and fetch()
differ in a couple of points, in order to allowfetch()
better control over the request and response:
- Promises returned from
fetch()
will not fail in any HTTP error status (even HTTP 404 or 500)- It will resolve successfully with
ok
status set tofalse
- It will only fail after a network failure or if the request couldn’t be completed.
- It will resolve successfully with
- By default,
fetch()
will not send cookies or credentials to the server- Using the
init
option, you can specify how to deal with cookies and credentials already stored in the client (omit
,include
,same-origin
) - Since Aug 25, 2017. The spec changed the default credentials policy to
same-origin
.
- Using the
Usage
The first argument forfetch()
is a URL to the resource, and optionally, a second parameter init
(an object with a set of request attributes):
var fetchResponsePromise = fetch(URL [, init] );
You can get the same result creating a Request
object, then calling fetch()
var request = new Request(URL [, init]) var fetchResponsePromise = fetch(request);
The result is a Promise
, which resolve to a Response
object. You can chain this call with then()
to use the resolved Response and catch()
to capture errors in the request/response process. For example, this is a common fetch()
call to retrieve and parse JSON data [https://codepen.io/fraigo/pen/GRKdwdR?editors=1111]:
fetch('https://myhost.com/api',{ credentials: 'same-origin' }) .then(function(response) { // use response.text() to continue with plain text // verify response checking (response.ok) return response.json() }).then(function(jsonData) { console.log('Parsed JSON', jsonData) }).catch(function(ex) { console.error('Error', ex.message) })
So, after getting the response
, it’s converted to a JSON object using response.json().
Then, the JSON object jsonData
could be used. If any error occurs, the exception ex
will contain error details. Using ES6 arrow functions syntax is even more clean:
fetch('https://myhost.com/api',{ credentials: 'same-origin' }) .then(response => response.json()) .then(jsonData => console.log('Parsed JSON', jsonData)) .catch(ex => console.error('Error', ex.message))
To see the difference, this is the same process using XMLHtttpRequest [https://codepen.io/fraigo/pen/XWrqyaV?editors=1111]
var httpRequest = new XMLHttpRequest(); httpRequest.onreadystatechange = function(){ if (httpRequest.readyState === XMLHttpRequest.DONE) { if (httpRequest.status === 200) { try { console.log('Response text', httpRequest.responseText); var jsonData=JSON.parse(httpRequest.responseText); console.log('Parsed JSON', jsonData); } catch (e) { console.error("Parse Error", httpRequest.statusText); } } else { console.error("Response Error", httpRequest.statusText); } } }; httpRequest.onerror = function(){ // will fail in HTTP 500 or 404 as well console.error("Request Error"); }; httpRequest.open('GET', 'myhost.com/api'); httpRequest.send();
The main differences are evident. Using XmlHTTPRequest
we have:
- Multiple points of failure/exceptions, different ways to catch them
- More lines of code, don’t follow a linear sequence
- One single object to manage the request and the response
In the other hand, using fetch():
- One point of failure detection, in the promise rejection.
- Clean code, followed sequentially
- Different objects to manage requests and responses (including headers and body)
In the next sections I show you the main concepts for the main components of the Fetch API: Request
, Response
and Headers
Request
The main element in the Javascript Fetch API is the Request
. The main properties are:
.method
(e.g., GET, POST, etc.).headers
(array ofHeader
elements ).credentials
(e.g., “omit”, “same-origin”, “include”).cache
(e.g., default, reload, no-cache).body
(request body).context
(e.g., audio, image, iframe, etc.).mode
(e.g., cors, no-cors, same-origin, navigate.)
var request = new Request( 'https://jsonplaceholder.typicode.com/todos/1', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded'}, credentials: 'same-origin', body: 'user=John&email=john@test.com' } );
Headers
Additionally, you can manipulate HTTP headers (in both Request
and Response
) for common operations:
.append()
to create or add a new header (headers with the same name).delete()
to delete a header value.entries()
returns an iterator to loop over all headers.forEach()
helps to iterate over each header element.get()
to get a specific header value.has()
allows to test if a header name is set.keys()
returns an iterator to loop over all header names
In the next example we set up a request using request Headers.
var myHeaders = new Headers(); myHeaders.append('Content-Type', 'text/json'); myHeaders.append('Authorization', 'Basic QWxhZGRpbjpPcGVuU2VzYW1l'); var myRequest = new Request('https://myhost.com/auth', {headers: myHeaders, method: 'post'})
Also, you can get and manage response Headers:
fetch(myRequest).then(function(response) { var resHeaders = response.headers; if (resHeaders.has('Auth-token')){ var myToken = resHeaders.get('Auth-token'); response.text().then(function(textContent) { resultMsg.innerText = textContent; }); } });
Response
Finally, you can process the Response
with a lot of options and methods:
.headers
Headers object associated with the response.ok
Will betrue
if the response was successful, with HTTP status 200-299.status
HTTP status code, (e.g., 200 for a success, 404 not found).statusText
Text related to the status code (e.g., OK for 200, Unauthorized for 401)- All
Body
methods, to manage the response content:.text()
A Promise to get simple text data.json()
To get parsed JSON data in an object.blob()
To processBlob
data (binary, raw). See Blob.arrayBuffer()
To obtain an array of raw bytes. See ArrayBuffer
In the next example we retrieve binary data in a Blob
, and create a data URL to display an image:
fetch('captcha.png', { cache: 'no-cache' }) .then(function(response) { return response.blob(); }).then(function(blob) { const imageURL = URL.createObjectURL(blob); captchaImage.src = imageURL; });
Compatibility
For those who need backwards compatibility with older browsers with no .fetch()
support, you could use a Javascript Fetch API polyfill (https://github.com/github/fetch).
In some cases you could also need support for promises. A Promise
polyfill is available for that case (https://github.com/taylorhakes/promise-polyfill)