Stop using XMLHttpRequest and switch to fetch
The most popular XHR API is XMLHttpRequest
which wasn’t really made for what we’ve been using it for. That’s why fetch
API has been created, the fetch
API is in some sort a modern replacement for XMLHttpRequest
. Let’s have a basic look at this window.fetch
method.
Browser compatibility
An important thing while developing a web application is the browser compatibility with the technologies used. As XMLHttpRequest
is older it logically have a better compatibility with older browsers compared to fetch
however, there is well done fetch
polyfill’s which makes this modern API compatible with older browsers such as IE, etc…
(c.f XMLHttpRequest Compatibility, Fetch Compatibility)
Basic XMLHttpRequest
Usage
XHR is a bit overcomplicated in my opinion and I still don’t understand why XML
is uppercase while Http
is camel-cased, it doesn’t make any sense at all. Anyways, here’s a common XHR usage
if (window.XMLHttpRequest) { // Mozilla, Safari, etc...
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 the request and send it.
request.open('GET', 'https://example.com/api', true);
request.send(null);
Of course JavaScript frameworks make XHR more pleasant to work with, but what you see above is a “simple” example of the most basic XHR usage. XHR is a real mess.
Basic fetch
Usage
The fetch
function is provided in the global window
scope, with the first argument being the URL (required) and the second the options (optional)
// fetch(url, options) | url: required - options: optional
fetch('https://example.com/api', {
method: 'get'
}).then(function(response) {
// Success :)
}).catch(function(err) {
// Error :(
});
And as you can see fetch
uses Javascript Promises in order to handle results/callbacks. If you aren’t used to Javascript Promises 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
const headers = new Headers();
// Add headers
headers.append('Custom-Header', 'MySuperValue');
headers.append('Content-Type', 'text/html');
// Check if this header is present
headers.has('Content-Type'); // true
headers.has('Some-Header'); // false
// Get the value of a specific header
headers.get('Custom-Header'); // MySuperValue
// Set a new value for an existing header
headers.set('Content-Type', 'text/plain');
// Delete a header
headers.delete('Custom-Header');
// Add initial values
const headers = new Headers({
'Content-Type': 'application/json',
'User-Agent': 'MySuperUserAgent'
});
In order to use request headers, you must first create a new Request
instance
const request = new Request('https://example.com/api', {
headers: new Headers({
'Content-Type': 'application/json',
'User-Agent': 'MyCustomUserAgent'
})
});
fetch(request).then(function(response) {
// process the response
}).catch(function(error) {
// process the error
});
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, HEAD, POST, PUT, DELETEurl
- URL of the requestheaders
- associated Headers objectreferrer
- referrer of the requestmode
- cors, no-cors, same-origincredentials
- should cookies go with the request? omit, same-originredirect
- follow, error, manualintegrity
- subresource integrity valuecache
- cache mode (default, reload, no-cache)
Here’s a sample of Request
usage
// Build the request
const request = new Request('https://example.com/anything', {
method: 'HEAD',
mode: 'no-cors',
redirect: 'follow',
headers: new Headers({
'Content-Type': 'text/html'
})
});
// And now use the request
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.
fetch('https://example.com/anything', {
method: 'HEAD',
mode: 'no-cors',
redirect: 'follow',
headers: new Headers({
'Content-Type': 'text/html'
})
}).then(function() {
// handle response
});
You’ll likely only use Request instances within Service Workers since the Request and fetch signatures can be the same.
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, corsurl
useFinalURL
- Boolean for if url 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.
// Fake response for service worker testing -- new Response(body, options)
const response = new Response('response body', {
ok: false,
status: 404,
url: '/'
});
// The fetch's then gets a Response instance back
fetch('https://exemple.com/').then(function(response) {
console.log('ok: ', response.ok); // false
});
The Response
also provides the following methods:
clone()
Creates a clone of a Response objecterror()
Returns a new Response object associated with a network errorredirect()
Creates a new response with a different URLarrayBuffer()
Returns a promise that resolves with an ArrayBufferblob()
Returns a promise that resolves with a BlobformData()
Returns a promise that resolves with a FormData objectjson()
Returns a promise that resolves with a JSON objecttext()
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://example.com/api/list.json').then(function(response) {
// Convert to JSON
return response.json();
}).then(function(jsObj) {
// jsObj is an javascript object from the json response
console.log(jsObj);
});
The json()
method is a simple shortcut to JSON.parse(jsonString)
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('/404').then(function(response) {
return response.text();
}).then(function(htmlresponse) {
// <!DOCTYPE ....
console.log(htmlresponse);
});
You can get the response text via chaining the Promise’s then
method along with the text()
method.
Handling Blob Responses
For example, loading image via fetch its a bit different
fetch('https://example.com/someimage.jpg').then(function(response) {
return response.blob();
})
.then(function(imageBlob) {
document.querySelector('img').src = URL.createObjectURL(imageBlob);
});
The blob()
method of the Body mixing takes a Response stream and reads it to completion.
Posting Form Data
AJAX is used a lot for sending form data, here’s how you would do it with the use of fetch
fetch('https://example.com/submit', {
method: 'post',
body: new FormData(document.getElementById('myForm'))
});
And if you want to post some JSON data
fetch('https://example.com/submit', {
method: 'post',
body: JSON.stringify({
some: document.querySelector('#some').value,
json: document.querySelector('#json').value,
data: document.querySelector('#data').value
})
});
Simple as that!
Polyfill
There is a lot of Polyfill’s for fetch method, but I highly suggest you to check the GitHub one.