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 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 requestheaders
- associatedHeaders
objectreferrer
- referrer of the requestmode
-cors
,no-cors
,same-origin
credentials
- should cookies go with the request?omit
,same-origin
redirect
-follow
,error
,manual
integrity
- subresource integrity valuecache
- 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 ifurl
is the final URLstatus
- 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.
window.fetch
went live in Chrome with yesterday’s Chrome Stable release.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.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.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.
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.
What is different between using
fetch
andjQuery.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 andjQuery.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!
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. >_<
Hey David, great writeup, thanks a lot!
I’m a bit confused about the JSON part though:
You say
Hey David, great writeup, thanks a lot!
I’m a bit confused about the JSON part though:
You say
is like calling
on the response body, but the next
takes a js object as a parameter — so isn’t it more like
then?
You’re right! Updated!
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”).
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!
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.
Maybe worth to note that with async/await in ES7 (or with task.js with ES6), it will be funnier:
Or with task.js:
I like the await syntax a lot.
Stop trying to make fetch happen. It’s not going to happen.
“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 :)
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.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: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 :)
This is one article I keep coming back to because I find it informative and clear. Thanks dude.
fetch()
really seems to be the way to go these days, thanks for the write up.Hi!
Every place i see samples of fetch, i see two calls to “then” chained, as you did in here
Is it needed? is it just a good practice? I mean, could you just have the following?
Thank you :-)
response.json()
returns a promise (representing the process to convert the string to JSON). That’s why you need the secondthen()
!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.
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.
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!
“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!
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?
When I run the example code from your post
in Firefox (version 47), it sends a request to
https://davidwalsh.name/[object%20Object]
. TheContent-Type
is nowhere to be seen, either. (And it should have beenAccept
, by the way.)My site uses MooTools, which is overriding Request()
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?
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.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”
Forgot source: https://en.wikipedia.org/wiki/Naming_convention_(programming)
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…
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.
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
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
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 theContent-Type
key ?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.
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.
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?
Cool, I’ve never heard of this. I made a JS library that uses promises for XMLHttpRequests (see my website), but this looks better!
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 byfetch
ing 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!)Hello! How can I retrieve data from a post method?
isset($_POST['email'])
seems to befalse
in a PHP script fetched by the JavaScript API.Is there any post function just like fetch?
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?So my fetch looks like this
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?
I’ve exactly the same problem. How can I solve it?
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
Probably a simpler question to answer: Will the browser render HTML returned from fetch POST calls? I admit this is an odd use case.
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?
this is part or maybe all of the why —
there are 2 single-quotes at end of url; chuck one of ’em.
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?
Cannot set my JWT using fetch API.
But works perfectly with postman if authorization token is sent via headers.
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
These comments though.. I should have made popcorn.
Chrome doesn’t let fetch file in same local directory without a server?
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.
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.
There are two helpfull functions to make possible perform correct fetch requsets with UTF-8 contents on most of languages:
For example we have to compare form data with base64 encode to make possible send any language contents to the server side: