JavaScript
Article

Introduction to the Fetch API

By Ludovico Fischer

For years, XMLHttpRequest has been web developers trusted sidekick. Whether directly or under the hood, XMLHttpRequest has enabled Ajax and a new type of interactive experiences, from Gmail to Facebook.

The Fetch API aims to replace XMLHttpRequest as the foundation of communication with remote resources. How this new API looks like and what problems it solves is the topic of this article.

The Fetch API

The Fetch API provides a fetch() method defined on the window object, which you can use to perform requests. This method returns a Promise that you can use to retrieve the response of the request.

To illustrate the Fetch API, we’ll use a few lines of code that retrieve photographs using the Flickr API and insert them into the page. At the time of writing, this API isn’t well supported. So, to have the code working I suggest you to try it using the last stable version of Chrome, which is version 43. Also note that the code needs you to replace your API key in place of the placeholder I set (“your_api_key”).

As the first task, you have to retrieve a few pictures of penguins from Flickr and display them in your page.

/* API URL, you need to supply your API key */
var URL = 'https://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=your_api_key&format=json&nojsoncallback=1&tags=penguins';

function fetchDemo() {
  fetch(URL).then(function(response) {
    return response.json();
  }).then(function(json) {
    insertPhotos(json);
  });
}

fetchDemo();

You pass the request URL string to the global fetch() method, which immediately returns a Promise. When the promise is fulfilled, it returns a Response object, which exposes a json() function to return the response s JSON object. response.json() returns a Promise object as well, so in our example we need to keep chaining with then().

For comparison, here’s the equivalent fetchDemo() function that uses XMLHttpRequest:

function xhrDemo() {
  var xhr = new XMLHttpRequest();
  xhr.onload = function() {
    insertPhotos(JSON.parse(xhr.responseText));
  };
  xhr.open('GET', URL);
  xhr.send();
}

The main difference is that you use event handlers instead of Promise objects and that starting a request takes a few more method calls on the xhr object.

So far, this looks like a slightly more compact way to achieve the same old thing. Let’s dig a little deeper.

Why XMLHttpRequest Needs a Replacement?

Looking at the example above, you might be wondering why you could not just use one of the existing XMLHttpRequest wrappers. The reason is that there is more the fetch API offers you than just the fetch() method.

While you must use the same instance of XMLHttpRequest to perform the request and retrieve the response, the fetch API lets you configure request objects explicitly.

You can create a new request object with the Request constructor function, which is also part of the proposed standard. The first argument is the request URL, and the second argument is an option object that configures the request. Once created, you pass the created object to the fetch method instead of using a string specifying the URL.

var req = new Request(URL, {method: 'GET', cache: 'reload'});
fetch(req).then(function(response) {
  return response.json();
}).then(function(json) {
  insertPhotos(json);
});

Here, we specified the request method explicitly and asked to never cache the response.

What’s cool about Request is that you can create a new request from an old one. The new request is the same as the old one, but you can tweak it for a different use case. For example, you can create a POST request from a GET request to the same resource. Here is an example:

var postReq = new Request(req, {method: 'POST'});

A Request object has an headers property that has as its value a Headers object. The latter lets you manipulate the request headers explicitly. In addition, you can access the response headers, but keep in mind that they are read-only values.

var headers = new Headers();
headers.append('Accept', 'application/json');
var request = new Request(URL, {headers: headers});

fetch(request).then(function(response) {
  console.log(response.headers);
});

In the previous code, you use the Headers constructor to obtain an object that you use to configure the request headers when you create a new Request object.

Similarly, you can create a Response:

function responseDemo() {
  var headers = new Headers({
    'Content-Type': 'application/json',
    'Cache-Control': 'max-age=3600'
  });
  
  var response = new Response(
    JSON.stringify({photos: {photo: []}}),
    {status: 200, headers: headers}
  );
  response.json().then(function(json) {
    insertPhotos(json);
  });
}

Request and Response follow the HTTP specification closely; you should recognize them if you’ve ever used a server-side language. But what’s the point of creating HTTP responses in the browser? After all, you’ve got nobody to send them to. Not so fast. You can send responses to yourself with the Service Workers API. Service Workers will allow you to build applications that work offline by intercepting HTTP requests from the browser and supplying responses that you build locally to replace those that would normally come from the server. Please note that at the time of writing, Service Workers are still experimental and thus subject to changes.

The Backlash

Because of the dubious success of some past efforts (the HTML5 Drag and Drop API stands accused of mediocrity, and Web Components of futility), a replacement for battle-tested XHR has incited perplexity.

One objection is that Promises miss important XMLHttpRequest use cases. For example, you cannot gather progress information or cancel a standard ES6 Promise. Fetch enthusiast developers have tried to imagine an extension of the Promise API to cancel a Promise. This proposal undermines the purpose a bit since fetch Promises won’t be totally standard any more. But maybe this work could influence a future cancellable Promise standard. On the other hand, by using an XMLHttpRequest you can monitor progress (by listening to a progress event) and also cancel it (with the abort() method). However, it also true that you can wrap it with a Promise if you need.

A second objection is that the Web platform requires more low-level than high-level APIs. The answer to it should be that the Fetch API is low-level enough that the current WHATWG spec defines the XMLHttpRequest.send() method in terms of a fetch Request object. The Fetch Response.body implements a getReader() method that gives incremental access to raw byte stream. For example, if your list of photos is so big that it could not fit into memory, you can proceed like in the example shown below:

function streamingDemo() {
  var req = new Request(URL, {method: 'GET', cache: 'reload'});
  fetch(req).then(function(response) {
    var reader = response.body.getReader();
    return reader.read();
  }).then(function(result, done) {
    if (!done) {
      // do something with each chunk
    }
  });
}

The handler function receives the response body chunk by chunk instead of all at once. The done flag is true when the body has been read in its entirety. This way you only need to handle one chunk at a time instead of the whole response body at once.

Unfortunately, it’s still early days for this Stream API and if you need to parse JSON this way you would need to implement a lot of logic from scratch.

Browsers Support

Fetch is supported in Chrome 42+, Opera 29+, and Firefox 39+. Microsoft marked this feature as under consideration. Ironically, XMLHttpRequest gets a replacement just as Internet Explorer finally implemented progress events for the response. Right now, if you need IE support (whether the E stands for Explorer or Edge), you’ll have to use a polyfill.

Conclusions

In this article you’ve seen what the new Fetch API looks like and what problems it solves. On the surface, this API looks simple, but it also seems to tie into lower-level features like Streams that make client-side programming a little bit more like systems programming.

What about you? Are you looking forward to using it? If you want to deepen this topic, you can find a working example for this article on GitHub.

In case you want to learn more about this topic, these are some interesting links:

Free Guide:

7 Habits of Successful CTOs

"What makes a great CTO?" Engineering skills? Business savvy? An innate tendency to channel a mythical creature (ahem, unicorn)? All of the above? Discover the top traits of the most successful CTOs in this free guide.

  • jaffathecake

    Please note that at the time of writing, Service Workers are still experimental and thus subject to changes

    ServiceWorkers have been in Chrome stable since January and are ready to use in production https://jakearchibald.github.io/isserviceworkerready/

    For example, you cannot gather progress information

    This is where streams come in. When you use progress events in XHR you’re heavily reliant on Content-Length being correct. Using streams makes this much more clear.

    Fetch enthusiast developers have tried to imagine an extension of the Promise API to cancel a Promise. This proposal undermines the purpose a bit since fetch Promises won’t be totally standard any more

    The cancelable promise would be a subclass, which is supported by ES6. The cancelable promise itself would be standardised either in ES-land or WHATWG land, so fully standard one way or the other.

    Unfortunately, it’s still early days for this Stream API and if you need to parse JSON this way you would need to implement a lot of logic from scratch.

    There’s already a JSON method, why would you want to reimplement this in streams?

  • WebReflection

    probably worth mentioning that IE already exposes Streams through XHR. Accordingly, today IE provides the most powerful/complete interface for “all the things” network related, and that indeed could be wrapped in a Promise and be somehow fetch-like https://msdn.microsoft.com/en-us/library/hh772328(v=vs.85).aspx

  • M S i N Lund

    Is there anything you can do with this, that you absolutely cant do without it?

  • Andreas Frömer

    Nice little article, thank u. Just notice there is a ‘ missing in the first header code block :)

    • Aurelio De Rosa

      Good catch. Fixed.

  • http://www.walabbady.com Waleed Alabbady

    Waiting for it to be in jquery ($.ajax….)

Recommended
Sponsors
Because We Like You
Free Ebooks!

Grab SitePoint's top 10 web dev and design ebooks, completely free!

Get the latest in JavaScript, once a week, for free.