Fetch API Featured Image
UI Development

Fetch API

For over a decade we have been using XMLHttpRequest to attain asynchronous requests in JavaScript. XHR is extremely useful but has its own disadvantages. XHR manages by interacting with one object, and state is tracked using events. Event-based model does’t play well with JavaScript’s recent specialize in Promise and generator-based asynchronous programming.

The Fetch API provides an interface for fetching resources across the networks. it’s familiar to XMLHttpRequest, but the new API provides a more powerful and versatile feature set.

The fetch() method is modern and versatile. Fetch API provides an improved alternative that may be easily employed by other technologies like ServiceWorkers. Fetch introduces 3 interfaces. These are HeadersRequest and Response. They map on to the underlying HTTP concepts but have certain visibility filters in situ for privacy and security reasons, like supporting CORS rules and ensuring cookies aren’t readable by third parties. it’s well supported by all modern browsers, but not supported by some old browsers (can be polyfilled).

Using fetch

Fetch API provides fetch method defined in window object, which returns a Promise that you simply can simply use can to retrieve the response of the request.

The basic syntax is:

fetch(url, [options]) 
  .then((response) => response.json()) 
  .then((data) => console.log(data)) 
  .catch((err) => console.error(err));

fetch() takes two arguments. First one is resource URL and second parameter options object which is optional. Without options, that’s a simple GET request, downloading the contents of the url.

Here is the list of fetch options with their default values (alternatives in comments):

  • method: “GET” // POST, PUT, DELETE, etc.
  • headers: Headers {
    // The content type header is typical auto-set depend on the request body
    “Content-Type”: “application/json”
  • body: undefined // FormData, string, Blob, BufferSource or URLSearchParams
  • referrer: “about:client” // or “” to send no Referrer header or same-origin URL
  • referrerPolicy: “no-referrer-when-downgrade”, // no-referrer, same-origin, origin, strict-origin, origin-when-cross-origin, strict-origin-when-cross-origin, or unsafe-url.
  • mode: “cors” // same-origin, no-cors
  • credentials: “same-origin” // omit, include
  • cache: “default” // no-store, reload, no-cache, force-cache, only-if-cached
  • redirect: “follow” // manual, error
  • integrity: “” // a hash, like “sha256-BpfBw7ivV8q2jLiT13fxDYAe22nFSE=”
  • keepalive: false // true
  • signal: undefined, // AbortController to abort request


Browser will execute fetch method immediately and returns a promise.

Getting a response from fetch is a two stage processes.

First promise returned by fetch will resolves with an object of response class as soon as the server responds with headers. At this stage we are able to check HTTP status if it is successful or not, but don’t have the body yet.

If you inspect the response in your browser’s console, you must see a Response object with several properties.

Screenshot of response in your browser’s console.

Second, to extract the body content from the response we need to use Body mixin methods, which is implemented by Request and Response object.

Body mixin has multiple methods to extract different types of body contents.

Methods/Properties Description Type
text() Read the response and return as text string
json() Read the response and parse the result as JSON string
formData()  Return the response as FormData object FormData
blob() Return the response as Blob (binary data with type), Blob/File
arrayBuffer() Return the response as ArrayBuffer (low-level representation of binary data) ArrayBuffer
body  body is a ReadableStream object, it allows you to read the body chunk-by-chunk. ReadableStream

Note: We can use body-reading method only once. If we’ve already got the response with response.text(), then response.json() won’t work again, because the body content has already been processed.

The most common response properties you’ll use are:

  • response.status — An integer (default value 200) containing the response status code.
  • response.statusText — A string (default value “OK”), which corresponds to the HTTP status code message.
  • response.ok — seen in use above, this can be a shorthand for checking that status within range 200-299 inclusive. This returns a Boolean.

Error handling

Handling errors with Fetch isn’t straightforward processes as handling in success messages. As fetch() returns a promise, we are able to use catch method of the promise to intercept an error which occur during the execution of the request.

fetch(url, [options]) 
  .then((response) => { 
    if(response.ok) { 
      return response.json(); 
    else { 
      return Promise.reject({ 
        status: response.status, 
        statusText: response.statusText 
  .then((data) => console.log(data)) 
  .catch((err) => { 
    if(err.status === ‘404’) { 
    } else { 

Promise returned from fetch wont rejects on HTTP error status even it’s 404 and 500 status code. Instead it’ll resolve normally (with ok status set to false), and it’ll reject on network failure or if anything prevented the request from completing.

Canceling fetch request

When fetch was introduced, there are two issue many JavaScript lovers used to comment.

  • There is no way to abort a request once opened.
  • There is no request timeout for fetch.


Due to continuous improvements in JavaScript ecosystem, new features AbortController and AbortSignal are introduced, a generic API to notify abort events. This solves the problem of aborting a request.

const controller = new AbortController(); 
const signal = controller.signal 

fetch(url, { signal });

Still there is no inbuilt feature for fetch() to set request timeout but we can make use of setTimeout() and above abort functionality to solve the request timeout functionality. 

setTimeout(() => contoller.abort(), 5000); // request timeout after 5 seconds

But setTimeout() will trigger abort() even fetch already returned. Handily, event fetch already returned, calling abort() won’t cause any error. When abort signal triggered, fetch will reject the promise with an DOMException named ‘AbortError’. Using this we differentiate the generic errors with request timeout errors.

fetch('https://jsonplaceholder.typicode.com/todos/1', {signal}) 
  .then((response) => response.json()) 
  .then((data) => console.log(data)) 
  .catch((error) => { 
    if(error.name === 'AbortError') { 
      console.error('Fetch aborted !!!'); 
    } else { 
      console.error('Error - ', error); 

We can improvise above abort process using Promise.race().

Simple custom fetch wrapper

We can build our own custom fetch wrapper by leveraging about code snippets. This can be used in small applications as an alternative for axios or any other sophisticated libraries. A simple custom fetch wrapper looks as shown below. It can be improvised based on our needs.

export default class fetchApi {
    static myRequest(url, method, options = {}) {
        const header = new Headers();
        const controller = new AbortController();
        const signal = controller.signal;

        // Sets content type to 'application/json' for POST,PUT,PATCH,DELETE requests 
        if (!header.get('content-type') && method !== 'GET') {
            header.set('content-type', 'application/json; charset=UTF-8')

        options.timeOut = options.timeOut === void 0 ? false : options.timeOut;
        options.parms = options.parms === void 0 ? false : options.parms;
        const opts = {
            'method': method,
            'signal': signal,
            'headers': header

        if (options.parms) {
            opts.body = JSON.stringify(options.parms);

        if (options.timeOut) {
            setTimeout(() => this.controller.abort(), options.timeOut);

        return new Promise((resolve, reject) => {
            fetch(url, opts).then((response) => {
                    if (response.ok) {
                        let contentType = response.headers.get('content-type')
                        if (contentType.includes('application/json')) {
                            return response.json();
                        } else if (contentType.includes('text/html')) {
                            return response.text();
                        } else {
                            throw new Error(`Sorry, content-type ${contentType} is not supported`)
                    } else {
                        return reject({
                            status: response.status,
                            statusText: response.statusText
                .then((result) => {
                    return resolve(result);
                .catch((error) => {
                    if (error.name === 'AbortError') {
                        return reject('Fetch aborted !!!');
                    } else {
                        return reject(error);

    static get(url, options = {}) {
        return this.myRequest(url, 'GET', options);

    static post(url, options = {}) {
        return this.myRequest(url, 'POST', options);

    static put(url, options = {}) {
        return this.myRequest(url, 'PUT', options);

    static patch(url, options = {}) {
        return this.myRequest(url, 'PATCH', options);

    static delete(url, options = {}) {
        return this.myRequest(url, 'DELETE', options);

We can use this typical library as shown below.


    .then(data => console.log(data)) 
    .catch(error => console.error(error));


let data = { 
        title: "foo", 
        body: "bar", 
        userId: 1 

    .post("https://jsonplaceholder.typicode.com/posts", { parms: data }) 
    .then(data => console.log(data)) 
    .catch(error => console.error(error));

This syntax will be similar for other methods provided. 


Building own fetch wrapper around fetch and upgrading it as per our needs can definitely change your thoughts of using sophisticated libraries like JQuery, Axios, etc. Fetch is still evolving, this is an experimental API that should not be used in production code.

We can contribute to the evolution of this API by participating in discussion on the WHATWG and the issues in the Fetch and ServiceWorker specifications.







About The Author