fetch API

By  on  

One of the worst kept secrets about AJAX on the web is that the underlying API for it, XMLHttpRequest, wasn't really made for what we've been using it for.  We've done well to create elegant APIs around XHR but we know we can do better.  Our effort to do better is the fetch API.  Let's have a basic look at the new window.fetch method, available now in Firefox and Chrome Canary.

XMLHttpRequest

XHR is a bit overcomplicated in my opinion, and don't get me started on why "XML" is uppercase but "Http" is camel-cased.  Anyways, this is how you use XHR now:

// Just getting XHR is a mess!
if (window.XMLHttpRequest) { // Mozilla, Safari, ...
  request = new XMLHttpRequest();
} else if (window.ActiveXObject) { // IE
  try {
    request = new ActiveXObject('Msxml2.XMLHTTP');
  } 
  catch (e) {
    try {
      request = new ActiveXObject('Microsoft.XMLHTTP');
    } 
    catch (e) {}
  }
}

// Open, send.
request.open('GET', 'https://davidwalsh.name/ajax-endpoint', true);
request.send(null);

Of course our JavaScript frameworks make XHR more pleasant to work with, but what you see above is a simple example of the XHR mess.

Basic fetch Usage

A fetch function is now provided in the global window scope, with the first argument being the URL:

// url (required), options (optional)
fetch('https://davidwalsh.name/some/url', {
	method: 'get'
}).then(function(response) {
	
}).catch(function(err) {
	// Error :(
});

Much like the updated Battery API, the fetch API uses JavaScript Promises to handle results/callbacks:

// Simple response handling
fetch('https://davidwalsh.name/some/url').then(function(response) {
	
}).catch(function(err) {
	// Error :(
});

// Chaining for more "advanced" handling
fetch('https://davidwalsh.name/some/url').then(function(response) {
	return //...
}).then(function(returnedValue) {
	// ...
}).catch(function(err) {
	// Error :(
});

If you aren't used to then yet, get used to it -- it will soon be everywhere.

Request Headers

The ability to set request headers is important in request flexibility. You can work with request headers by executing new Headers():

// Create an empty Headers instance
var headers = new Headers();

// Add a few headers
headers.append('Content-Type', 'text/plain');
headers.append('X-My-Custom-Header', 'CustomValue');

// Check, get, and set header values
headers.has('Content-Type'); // true
headers.get('Content-Type'); // "text/plain"
headers.set('Content-Type', 'application/json');

// Delete a header
headers.delete('X-My-Custom-Header');

// Add initial values
var headers = new Headers({
	'Content-Type': 'text/plain',
	'X-My-Custom-Header': 'CustomValue'
});

You can use the append, has, get, set, and delete methods to modify request headers. To use request headers, create a Request instance :

var request = new Request('https://davidwalsh.name/some-url', {
	headers: new Headers({
		'Content-Type': 'text/plain'
	})
});

fetch(request).then(function() { /* handle response */ });

Let's have a look at what Response and Request do!

Request

A Request instance represents the request piece of a fetch call. By passing fetch a Request you can make advanced and customized requests:

  • method - GET, POST, PUT, DELETE, HEAD
  • url - URL of the request
  • headers - associated Headers object
  • referrer - referrer of the request
  • mode - cors, no-cors, same-origin
  • credentials - should cookies go with the request? omit, same-origin
  • redirect - follow, error, manual
  • integrity - subresource integrity value
  • cache - cache mode (default, reload, no-cache)

A sample Request usage may look like:

var request = new Request('https://davidwalsh.name/users.json', {
	method: 'POST', 
	mode: 'cors', 
	redirect: 'follow',
	headers: new Headers({
		'Content-Type': 'text/plain'
	})
});

// Now use it!
fetch(request).then(function() { /* handle response */ });

Only the first parameter, the URL, is required. Each property becomes read only once the Request instance has been created. Also important to note that Request has a clone method which is important when using fetch within the Service Worker API -- a Request is a stream and thus must be cloned when passing to another fetch call.

The fetch signature, however, acts like Request so you could also do:

fetch('https://davidwalsh.name/users.json', {
	method: 'POST', 
	mode: 'cors', 
	redirect: 'follow',
	headers: new Headers({
		'Content-Type': 'text/plain'
	})
}).then(function() { /* handle response */ });

You'll likely only use Request instances within Service Workers since the Request and fetch signatures can be the same. ServiceWorker post coming soon!

Response

The fetch's then method is provided a Response instance but you can also manually create Response objects yourself -- another situation you may encounter when using service workers. With a Response you can configure:

  • type - basic, cors
  • url
  • useFinalURL - Boolean for if url is the final URL
  • status - status code (ex: 200, 404, etc.)
  • ok - Boolean for successful response (status in the range 200-299)
  • statusText - status code (ex: OK)
  • headers - Headers object associated with the response.
// Create your own response for service worker testing
// new Response(BODY, OPTIONS)
var response = new Response('.....', {
	ok: false,
	status: 404,
	url: '/'
});

// The fetch's `then` gets a Response instance back
fetch('https://davidwalsh.name/')
	.then(function(responseObj) {
		console.log('status: ', responseObj.status);
	});

The Response also provides the following methods:

  • clone() - Creates a clone of a Response object.
  • error() - Returns a new Response object associated with a network error.
  • redirect() - Creates a new response with a different URL.
  • arrayBuffer() - Returns a promise that resolves with an ArrayBuffer.
  • blob() - Returns a promise that resolves with a Blob.
  • formData() - Returns a promise that resolves with a FormData object.
  • json() - Returns a promise that resolves with a JSON object.
  • text() - Returns a promise that resolves with a USVString (text).

Handling JSON

Let's say you make a request for JSON -- the resulting callback data has a json method for converting the raw data to a JavaScript object:

fetch('https://davidwalsh.name/demo/arsenal.json').then(function(response) { 
	// Convert to JSON
	return response.json();
}).then(function(j) {
	// Yay, `j` is a JavaScript object
	console.log(j); 
});

Of course that's a simple JSON.parse(jsonString), but the json method is a handy shortcut.

Handling Basic Text/HTML Responses

JSON isn't always the desired request response format so here's how you can work with an HTML or text response:

fetch('/next/page')
  .then(function(response) {
    return response.text();
  }).then(function(text) { 
  	// <!DOCTYPE ....
  	console.log(text); 
  });

You can get the response text via chaining the Promise's then method along with the text() method.

Handling Blob Responses

If you want to load an image via fetch, for example, that will be a bit different:

fetch('https://davidwalsh.name/flowers.jpg')
	.then(function(response) {
	  return response.blob();
	})
	.then(function(imageBlob) {
	  document.querySelector('img').src = URL.createObjectURL(imageBlob);
	});

The blob() method of the Body mixin takes a Response stream and reads it to completion.

Posting Form Data

Another common use case for AJAX is sending form data -- here's how you would use fetch to post form data:

fetch('https://davidwalsh.name/submit', {
	method: 'post',
	body: new FormData(document.getElementById('comment-form'))
});

And if you want to POST JSON to the server:

fetch('https://davidwalsh.name/submit-json', {
	method: 'post',
	body: JSON.stringify({
		email: document.getElementById('email').value,
		answer: document.getElementById('answer').value
	})
});

Very easy, very eye-pleasing as well!

Unwritten Story

While fetch is a nicer API to use, the API current doesn't allow for canceling a request, which makes it a non-starter for many developers.

The new fetch API seems much saner and simpler to use than XHR.  After all, it was created so that we could do AJAX the right way; fetch has the advantage of hindsight.  I can't wait until fetch is more broadly supported!

This is meant to be an introduction to fetch.  For a more in depth look, please visit Introduction to Fetch.  And if you're looking for a polyfill, check out GitHub's implementation.

Recent Features

  • By
    5 Ways that CSS and JavaScript Interact That You May Not Know About

    CSS and JavaScript:  the lines seemingly get blurred by each browser release.  They have always done a very different job but in the end they are both front-end technologies so they need do need to work closely.  We have our .js files and our .css, but...

  • By
    fetch API

    One of the worst kept secrets about AJAX on the web is that the underlying API for it, XMLHttpRequest, wasn't really made for what we've been using it for.  We've done well to create elegant APIs around XHR but we know we can do better.  Our effort to...

Incredible Demos

  • By
    Spoiler Prevention with CSS Filters

    No one likes a spoiler.  Whether it be an image from an upcoming film or the result of a football match you DVR'd, sometimes you just don't want to know.  As a possible provider of spoiler content, some sites may choose to warn users ahead...

  • By
    iPad Detection Using JavaScript or PHP

    The hottest device out there right now seems to be the iPad. iPad this, iPad that, iPod your mom. I'm underwhelmed with the device but that doesn't mean I shouldn't try to account for such devices on the websites I create. In Apple's...

Discussion

  1. window.fetch went live in Chrome with yesterday’s Chrome Stable release.

  2. MaxArt

    One of the features that are lost with fetch is monitoring the progression of the upload/download. And I don’t quite know if it can be restored nicely with a promise-based interface.

  3. I can’t wait until Spartan 2.0? becomes the minimum browser we need to support! (Until then window.fetch simply isn’t an option without coding a fallback for IE support) http://caniuse.com/#search=fetch That all said, I seriously can’t wait – it will be awesome to be able to use it.

    • Tony

      Agreed, can’t wait to start using it. Don’t mind writing a fallback if need be, this is forward progress =>

    • All IE versions and *sadly* Safari don’t support fetch API yet. The syntax and how it works are very interesting. Can’t wait to use it in my projects.

  4. Dave

    David, I read your posts and I get confused feelings. I’m excited using the code in the examples but feel like a bad developer for not knowing this was available to me already. Then I find out it’s actually not available to me. Might be nice to play with but not for use in production and it would be nice if it was noted that this exciting new API is new and not available in most browsers. I’m still not even using native promises because of legacy browsers. jQuery has let me get use to coding using promises, and when it’s available natively in all the browsers I need to support, I’ll move native.

    Just try and make it clear that use of the example code may not be available in all browsers just yet. Maybe you could add tags for browsers that support whatever API or other features you are discussing.

    • Please re-read the last sentence of the intro.

  5. deDogs

    What is different between using fetch and jQuery.ajax? Do we really needing something else on the global scope, which adds additional project weight. Some occasions fetch may not be needed in a project.

    • fetch is native and jQuery.ajax is a library method.

      If you’re looking to remove something from the global scope, jQuery is probably the one to remove. :)

    • haha I agree. Although I hate jquery for stealing code to claim but hey whatever!. You just re-share and respost other peoples work David. Lets not get big headed we all do it. jQuery just simply removed you from the pic and in return they saved users time. I enjoy coding my won faster more direct to the punch functions and handles my self. I am only complaining because you are a cocky prick. You’re welcome!

    • Blair

      I think you might be having a stroke. Please call a doctor.

      Also, this entire article has nothing to do with jQuery. The fetch API is built into the global scope of Firefox and Chrome Canary. Futhermore, David isn’t “repost other peoples work”, he’s writing a blog post on how the API works.

      I can’t even begin to respond to how asinine deDogs’s question was; jQuery is an external library which has nothing to do with the fetch API, it has nothing to do with “project weight”, and if you’re writing an application being run in Chrome or Firefox and not making a single ajax call, then what are you even writing (aside from a 100% offline app)?

      It baffles me how people like this even figure out how to leave comments. >_<

  6. Hey David, great writeup, thanks a lot!
    I’m a bit confused about the JSON part though:
    You say

  7. Hey David, great writeup, thanks a lot!
    I’m a bit confused about the JSON part though:
    You say

    return response.json()

    is like calling

    JSON.stringify()

    on the response body, but the next

    then

    takes a js object as a parameter — so isn’t it more like

    JSON.parse(responseBody)

    then?

  8. I don’t understand why you are omitting that progress notification as well as an equivalent of abort are missing.

    I also don’t understand why you wen’t full polyfill down to IE 5 for XHR, underlying how mess is that, and didn’t show how worst would be the full polyfill down to IE 5 for fetch, including the Promises polyfill.

    It looks like the community is putting effort to emphasize so much how bad is XHR …. this is the part I like the least about the entire fetch story.

    • I added a block about the inability to cancel! :)

    • Not sure if you heard about the SAM pattern and the corresponding SAFE middleware, it supports cancellation (as in ignore the response) in a generic and very simple way (what I call “action hang back”).

  9. and could you also drop that polyfill which is completely unfair or out of context? Do you really have to show that? Because it’s not clear what’s the point.

    Show 6 LOC to get fetch cross platform down to old IE and in few lines of code, including standard Promise polyfill, or maybe simply show the difference between APIs.

    XHR is just fine, where does Fetch API shine? Why should we consider it? Which browser is compatible? How big is the polyfill?

    The argument XHR is ugly, with all the problems we have on the Web, feels so childish.

    Apologies for the comment, I just don’t understand this “mafia” style war against XHR.

    “There’s a new API, it does things in a Promise friendly way, here how to use it => ” would be that kind of post I’d expect from your blog.

    Not another “XHR is ugly, long glory to the beautiful Fetch API”, I am sure you know what I mean.

    Regards

    • I just re-read my post, thinking by your comments that I may have said something too harsh, but I’m not seeing it…

      “One of the worst kept secrets about AJAX on the web is that the underlying API for it, XMLHttpRequest, wasn’t really made for what we’ve been using it for.” – I feel that’s true.

      “We’ve done well to create elegant APIs around XHR but we know we can do better. Our effort to do better is the fetch API.”” – I feel that’s also true.

      Nowhere did I use the word “ugly”. I did use “mess”, but getting XHR is a bit of a mess, no? Three different checks?

      I’m appreciate of XHR and how we’ve used it, but I’m excited about a new API which was developed with XHR history in mind.

    • you don’t have 3 different checks … nobody does. Same you don’t have different checks to grab fetch, because it’s not cross platform. XHR is cross platform, and if you want to support IE < 9 you need only 2 extra checks. What do you need to do in order to have fetch cross platform? just 2 checks and few lines to have it? Or you need the entire fetch polyfill plus the entire Promises polyfill? What is the point of showing a polyfill for a well supported API, which is XHR, when the new one is not even supported at all? This is the part I don't understand, you are saying: here, few lines of code to have a cross browser API that works down to IE5 … compare against one that does not even exists, how better is the new one?

      I don't know … because the new one does not show how many lines of code you need to have it, while XHR can be shimmed in just those few lines.

      So, compare XHR, its ease everyone knows, its cross browser compatibility, against something that will never work down to IE < 9 and every other Mobile Web browser … how pointless is this comparison? Do you really not get it? You compared a library to polyfll an API agtainst an API that will need more libraries in order to be supported, than just few lines.

      I don't how else I can explain this, sorry.

    • I understand your viewpoint now. I’ll think on it, re-evaluate what you’ve written, and update my post as I see fit. Thanks!

    • LuisCarlos

      Thanks David for a GREAT post!. I think the problem Andrea is having is that bashing or relegating XHR to the trash heap is because it was invented by Microsoft and anything from Microsoft is kind of useless, ugly and well you know the rest. The fact of the matter is that technology moves on and we’re the better for it just like I’m grateful to Microsoft for giving us such great tool as XHR. We need your articles because it moves the rest of us into the present with a clear picture of what is coming in the future and that AIN’T BAD.
      thanks again and have a Happy new 2018.

    • or to make it a TL;DR … if just getting XHR is considered a mess for few lines of code, show how mess would be to have fetch API compatible with the same amount of browsers in just few lines of code. Here the catch: you cannot. If you need only 3 checks to polyfill XHR, you need 10X the amount of code and libraries to have Fetch API equivalently compatible and supported cross platform and cross browsers. Which part I am not explaining properly here?

    • Andrea: I understand what you’re saying. Chill.

    • One particular thing in which I totally agree with Andrea here is that XMLHttpRequest example is completely unfair as ActiveX polyfill is not needed for IE>6 (and modern projects usually have even higher bar, at least IE8+). This part should be updated.

    • “when the new one is not even supported at all?” – this is not true. Already using in FF and Chrome without polyfills.

  10. Florent

    Maybe worth to note that with async/await in ES7 (or with task.js with ES6), it will be funnier:

    (async function() {
      let response = await fetch('http://davidwalsh.name/demo/arsenal.json');
      let json = await response.json();
      console.log(json);
    })();
    

    Or with task.js:

    spawn(function* () {
      let response = yield fetch('http://davidwalsh.name/demo/arsenal.json');
      let json = yield response.json();
      console.log(json);
    }
    
    • I like the await syntax a lot.

  11. Ambrose

    Stop trying to make fetch happen. It’s not going to happen.

    • Sean

      “Stop trying to make X happen. It’s not going to happen.”
      Yep, let’s just abandon all technological progression because it isn’t going to be immediately used en masse.

    • He’s quoting a funny movie :)

  12. vsync

    What about cross-domain? This is totally ignored in this article and comments, oddly :/

    • It’s just another API for same old good XMLHttpRequest, cross-domain behaves just like always did.

  13. Don’t forget, by default, the fetch() won’t send cookies like good old jQuery did. To change that use {credentials: 'same-origin'}, like this:

     fetch('/amisignedin', {credentials: 'same-origin'})
     .then(....)
    • Updated Peter! Thank you!

    • Thank you so very, very much!

      I am building an application with a React frontend fetching data from a Java Spring backend and was on brink of despair not understanding why my REST calls were redirected to the login page although client authentication had been successful.

      Now its work perfectly :)

  14. Mike Holloway

    This is one article I keep coming back to because I find it informative and clear. Thanks dude.

  15. fetch() really seems to be the way to go these days, thanks for the write up.

  16. Miguel

    Hi!

    Every place i see samples of fetch, i see two calls to “then” chained, as you did in here

    fetch('http://davidwalsh.name/demo/arsenal.json').then(function(response) { 
    	// Convert to JSON
    	return response.json();
    }).then(function(j) {
    	// Yay, j is a JavaScript object
    	console.log(j); 
    });
    

    Is it needed? is it just a good practice? I mean, could you just have the following?

    fetch('http://davidwalsh.name/demo/arsenal.json').then(function(response) { 
    	// Convert to JSON
    	let j = response.json();
            return j.aProperty || whateverWith(j);
    });
    

    Thank you :-)

    • response.json() returns a promise (representing the process to convert the string to JSON). That’s why you need the second then()!

    • It’s not too clear in this article, but at the time the first promise resolves, we do NOT YET have the response body (the part that will be parsed to JSON) so you can think of .json() part as something like .setParserToUseJSON() and then returns another promise when the body has been received and parsed.

      I didn’t understand the fetch API well until I finally understood promises. A then handler that returns another promise is special and really unlocks the power of promises, but it’s not easy for a newcomer to understand by reading example code!

      @DavidWalsh: I think it might be confusing to readers to say “the resulting callback data has a json method […] Of course that’s a simple JSON.parse(jsonString), but the json method is a handy shortcut.”

      It’s not really a shortcut for JSON.parse alone, but does some magic with response buffering and parsing and eventually *resolves* to a result similar to that of calling JSON.parse!

      Might need some more explaining because I remember when I first came to fetch thinking “This is complicated by all this Promise stuff and double then.. why don’t we just have a simple node-style callback?” But now I understand how this abstracts the underlying process into a nice Promise API.

    • Sangeet Agarwal

      wrote a pen to help understand what might be happening behind the scenes, thought I should share it, I think this should approximate what mitht be happening …
      See the Pen fetch Api pseudo code by Sangeet (@Sangeet) on CodePen.”>http://codepen.io/Sangeet/pen/BzJOwj/” rel=”nofollow”>fetch Api pseudo code by Sangeet (@Sangeet) on CodePen.

      fetch Api pseudo code by Sangeet (@Sangeet) on CodePen." data-user="Sangeet">
  17. Hey David! Just a heads up: looks like text starting with “Response” heading is incorrectly wrapped inside h2 up to the next heading.

    P.S. Thanks for awesome reading!

  18. “One of the worst kept secrets about AJAX on the web is that the underlying API for it, XMLHttpRequest, wasn’t really made for what we’ve been using it for.”

    I feel like this is so true for _everything_ on the web :)

    Great article, David!

  19. Dusty

    I would like to access the certificate on the https server I connect to, to verify its fingerprint.

    Is it possible to id using fetch? If not, how could I do it?

  20. Johan

    When I run the example code from your post

    var request = new Request('/some-url', {
    	headers: new Headers({
    		'Content-Type': 'text/plain'
    	})
    });
    
    fetch(request).then(function() { /* handle response */ });

    in Firefox (version 47), it sends a request to https://davidwalsh.name/[object%20Object]. The Content-Type is nowhere to be seen, either. (And it should have been Accept, by the way.)

    • My site uses MooTools, which is overriding Request()

  21. Andrew

    Hey David,

    Great site and very clear – and it was one of the top google results!

    I’m using fetch now and finding it great – except I have no idea how to get the error when we catch! I get TypeError “failed to fetch” and that’s it. I’m using TypeScript, but I would have expected a response code, and message?

    I’d love to be able to indicate to my users why their POST failed, but without the ability to retrieve that message I’m lost. I’ve tried googling it and finding no help – any ideas?

    • Matthew Slight
      const fetch = require('node-fetch')
      const url = 'http://localhost:3000/api/v1/user';
      const opt = { method: 'get' }
      fetch(url, opt).then(res => {
        if(res.ok) {
          return res.json();
        }
        throw new Error('Network response was not ok.');
      }).then(json => {
        console.log(json)
      }).catch(err => {
        console.log('There has been a problem with your fetch operation: ', err.message);
      });
      
  22. Qi Fan

    If used within a web worker, JSON.parse(longLongString) blocks the main GUI thread in certain browsers, for whatever reason.

    The .json() on the other hand, doesn’t affect a different thread.

  23. You don’t need to start on XMLHttpRequest upper/lower case…

    “The built-in JavaScript libraries use the same naming conventions as Java. Data types and constructor functions use upper camel case (RegExp, TypeError, XMLHttpRequest, DOMObject) and methods use lower camel case (getElementById, getElementsByTagNameNS, createCDATASection). In order to be consistent most JavaScript developers follow these conventions.[citation needed] See also: Douglas Crockford’s conventions”

  24. Ehh, talk about the “overcomplicated” XMLHttpRequest API (to which I agree), but then you have to create a NEW HEADERS OBJECT to set Headers? Uhm…

  25. Thanks for the really useful overview. I’m enjoying experimenting with fetch along with a Polyfill.

    Small tweak that I’m sure everyone has just quickly fixed upon experimenting with your examples, but just a note that in the final ‘And if you want to POST JSON to the server’ example there should be a comma separator between the two JSON properties.

  26. Just another heads up, if you’re wanting to include credentials cross domain (for example our API server sits on a subdomain different to the front end app) the correct value for credentials is

    {credentials: 'include'}
  27. David, this is an awesome article. Thanks for writing it.

    Quick question: In Firefox, when I’m trying to use the .then() function and log my response, I’m often getting an object that just has

    : pending

    Not certain what’s going on and wondered if you had any ideas.

    Thanks!

    -Eric

  28. I tried posting json to server but somehow I wasnt able to find it req.body but after adding content-type I was able to get the data on server side. In your post json to server example, have you intentionally left out the Content-Type key ?

  29. Fetch API cannot load https://davidwalsh.name/flowers.jpg. No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://localhost:56028’ is therefore not allowed access. The response had HTTP status code 404. If an opaque response serves your needs, set the request’s mode to ‘no-cors’ to fetch the resource with CORS disabled.

  30. oliver

    What is the best way of passing url query parameters as an object when using fetch? I know that this is not something that the fetch API supports.

  31. 
    import 'whatwg-fetch';
    // fetch polyfill
    
    const url = https://abc.xgqfrms.xyz/some-url;
    
    const headers = new Headers({
        'Content-Type': 'text/plain',
        'X-My-Custom-Header': 'CustomValue'
    });
    
    const datas = JSON.stringify(
        {
            user: "username",
            pwd: "password"
        }
    );
    
    const method = POST;
    
    const request = new Request(url, {
        headers: headers,
        method: 'POST', 
        mode: 'cors', 
        redirect: 'follow',
        body: datas
    });
    
    fetch(
        request
    ).then(
        (response) => response.json()
    ).catch(
        (err) => {
            console.log(err, err);
            throw new Error("Whoops, An Error occurred!");
        }
    );
    
    
  32. I was stuck for a while on this, but I realized that setting:

    'Content-Type': 'application/json'

    in the request headers is required for the server to parse the POST/PUT/… body, unless for example body-parser is configured to accept 'text/plain' (in express’ case)

    Could the article be amended to point this out?

  33. Cool, I’ve never heard of this. I made a JS library that uses promises for XMLHttpRequests (see my website), but this looks better!

  34. Jordan
    // jQuery
    $.getJSON('/users').then(success, fail)
    
    // Axios
    axios.get('/users').then(success, fail);
    
    // Backbone
    users.fetch().then(success, fail)
    
    // Fetch (hey, you need to download 4-5kb min+gz of Fetch and Promise polyfills if you have IE<=11 or older mobile browsers)
    // There's actually more request config & response verification that should be done if you want it as robust as the xhr libs
    // And you can't cancel these, but they do stream
    fetch('/users', {credentials: 'same-origin'})
      .then(function(response) {
        if (response.ok) {
          return response.json();
        }
        else {
          // Reject the response since it's not a 200-level response (i.e. there's no usable data here)
          throw new Error('Throw or else you'll be trying to response.json() on 404s!');
        }
      })
      .then(success)
      .catch(fail);
    

    Fetch is good low-level tech just like XHR. But like XHR, you still need a layer of abstraction or else you’re code won’t be DRY and will stink, badly.

    I don’t understand why articles like this push the Kool-Aid. Even Jake Archibald (who I like very much) fails to bring up the annoying check for response.ok in his defense of fetch or the oddness of deleting a resource by fetching it (https://jakearchibald.com/2015/thats-so-fetch/). People are so quick to praise the “new” as they dismiss the “old” and no one ever does an honest follow-up to say, “Ok, I fell off the bandwagon and New Thing is not as cool as I made it out to be. It’s cool, but I oversold it. Old Thing, you actually knew what you were doing didn’t you? Trade-offs people, trade-offs…”

    Clearly, straight fetch is not something you should be writing. It requires a lib (and polyfills) and as such it has to compete with the existing xhr solutions and no dev should doubt themselves for going with $.ajax or axios or writing their own fetch lib (and share it, because the Kool-Aid is still being drunk years later!)

  35. Lucas

    Hello! How can I retrieve data from a post method? isset($_POST['email']) seems to be false in a PHP script fetched by the JavaScript API.

  36. Is there any post function just like fetch?

  37. Ryan Scharfer

    Hi David,

    I was helping you could help me out. I am using fetch() to make a POST call on the client side, and Express on the server side to respond to it. It seems like my server side code is doing it’s job, namely send back HTML as a result to the POST call. When I open up the dev console, I see the HTML in the response. But for whatever reason, the browser refuses to render the HTML that comes back. At first I thought it was a "credentials": "same-origin" issue, but even with that the browser won’t render the html. Any ideas?

  38. Ryan Scharfer

    So my fetch looks like this

    fetch("/course_1", {
                method:'POST',
                body:JSON.stringify(form),
                headers:{
                    "Content-Type": "application/json",
                    "Accept":"text/html"
                  
                },
              credentials: 'same-origin',
            })
            .then((res)=>{
                return res.text() 
                
            }).then(text=>console.log(text)) 
    
    
    // and my Express code looks like this..
    app.post('*', (req,res) => {
    
        res.render('index');
    
    })
    

    The HTML is returned as it should be — i can see this in the dev tools, but it’s just not being rendered. I am starting to think that browsers will not render HTML returned by fetch requests. Is that correct?

    • José Espejo Roig

      I’ve exactly the same problem. How can I solve it?

    • José Espejo Roig

      I’ve used in second clause document.write(text) and it works (render the new page) but the browser address window doesn’t change to the new page and the history browser doesn,t include the new page.
      I don’t like this solution

  39. Ryan Scharfer

    Probably a simpler question to answer: Will the browser render HTML returned from fetch POST calls? I admit this is an odd use case.

  40. Kevin

    I feel incredibly studip because I have done api calls in the past but not with fetch. I am working on a simple script pasted below. The response appears in the console log briefly but then becomes hidden in the chrome console.log. Why am I doing wrong with fetch?

    fetch('http://localhost:8080/wp-json/menus/v1/menus/primary'', {
    	method: 'get'
    }).then(function(response) {
    	console.log(response);
    }).catch(function(err) {
    	// Error :(
    });
    
    • JMedina

      this is part or maybe all of the why —
      there are 2 single-quotes at end of url; chuck one of ’em.

  41. Out of the topic. Just curious seeing there is no date shown in user comments. Is it by a purpose? How you manage the conversation then?

  42. Alnoor

    Cannot set my JWT using fetch API.

    'Authorization': "Bearer "+sessionStorage.jwt
    
    client : 
        const defaultHeaders = {
            'Authorization': "Bearer "+sessionStorage.jwt,
            'Content-Type': 'application/json',
            'Accept': 'application/json',
        };
    
        return fetch(url, { 
               method: 'post', 
               mode: 'cors',
               withCredentials: true,
               headers: defaultHeaders
             })
             .then(responseObject => {})
    
    
    server : (req.headers)
        { host: 'localhost:3006',
          connection: 'keep-alive',
         'access-control-request-method': 'POST',
          origin: 'http://localhost:3005',
         'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36',
         'access-control-request-headers': 'access-control-allow-credentials, access-control-allow-origin, authorization, content-type, x-my-custom-header',
         accept: '*/*',
         referer: 'http://localhost:3005/searchBookedGuest',
         'accept-encoding': 'gzip, deflate, sdch',
         'accept-language': 'en-GB,en-US;q=0.8,en;q=0.6' 
    }
    
    //No Authorization Token Found.
    

    But works perfectly with postman if authorization token is sent via headers.

  43. 
    # fetch & progress
    
    https://github.com/AnthumChris/fetch-progress-indicators
    
    
  44. Robert

    same curiosity about comments date :-)

    though at the beginning of the post, maybe you could head to a contemporary post on this topic

    since now is February 2019

    do you have one?
    are you going to publish one?

    thank you for your articles, always interesting and one step beyond

  45. Dan

    These comments though.. I should have made popcorn.

  46. Pauli Sudarshan Terho

    Chrome doesn’t let fetch file in same local directory without a server?

  47. Ryan

    At the end, you stated:

    “While fetch is a nicer API to use, the API current doesn’t allow for canceling a request, which makes it a non-starter for many developers.”

    But it appears that you have created the following article:

    > https://davidwalsh.name/cancel-fetch

    You should update that blurb and link to that post.

  48. Pradeep

    I am using Fetch method to logout from the application. Below is the code :

    var vUrl = m.url;
    fetch(vUrl).then(function (response) {
    console.log(‘ErrorR–>’ + response);
    }).then(function (text1) {
    console.log(‘Error–>’ + text1);
    });

    However this is not working and Chrome developer tools shows that request status is canceled.

    Kindly advice.

  49. There are two helpfull functions to make possible perform correct fetch requsets with UTF-8 contents on most of languages:

    
    // Multilaguage support of BTOA
    utoa: (s) => btoa(encodeURIComponent(s)),
    
    // Multilaguage support of ATOB
    atou: (s) => decodeURIComponent(self.atob(s)),
    
    

    For example we have to compare form data with base64 encode to make possible send any language contents to the server side:

    data.append( btoa(j.name), RR.utoa(j.value +'~:::~'+ j.type +'~:::~'+ ( j.maxLength ? j.maxLength : -1)) );
    

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!