The Fetch API is a promise-based JavaScript API for making asynchronous HTTP requests in the browser similar to XMLHttpRequest (XHR). Unlike XHR, it is a simple and clean API that uses promises to provides a more powerful and flexible feature set to fetch resources from the server.

Fetch is pretty much standardized now and is supported by all modern browsers except IE. If you need to support all browsers including IE, just add a polyfill released by GitHub to your project.

API Usage

Using Fetch API is really simple. Just pass the URL, the path to the resource you want to fetch, to fetch() method:

fetch('/js/users.json')
    .then(response => {
        // handle response data
    })
    .catch(err => {
        // handle errors
    });

We pass the path for the resource we want to retrieve as a parameter to fetch(). It returns a promise that passes the response to then() when it is fulfilled. The catch() method intercepts errors if the request fails to complete due to network failure or any other reason.

GET Request

By default, the Fetch API uses GET method for asynchronous requests. Let's use the Reqres REST API to retrieve a list of users using GET request:

fetch('https://reqres.in/api/users')
    .then(res => res.json())
    .then(res => {
        res.data.map(user => {
            console.log(`${user.id}: ${user.first_name} ${user.last_name}`);
        });
    });

The above request prints the following on the console:

1: George Bluth
2: Janet Weaver
3: Emma Wong

Calling fetch() method returns a promise. The response returned by the promise is a stream object which means that when we call json() method, it returns another promise. Call to json() method indicates that we are expecting a JSON response. If you are expecting an XML response, you should use text() method.

POST Request

Just like Axios, Fetch also allows to use any other HTTP method in the request: POST, PUT, DELETE, HEAD and OPTIONS. All you need to do is set the method and body parameters in the fetch() options:

const user = {
    first_name: 'John',
    last_name: 'Lilly',
    job_title: 'Software Engineer'
};

const options = {
    method: 'POST',
    body: JSON.stringify(user),
    headers: {
        'Content-Type': 'application/json'
    }
}

fetch('https://reqres.in/api/users', options)
    .then(res => res.json())
    .then(res => console.log(res));

The Reqres API sends us the body data back with an ID and created timestamp attached:

{  
   "first_name":"John",
   "last_name":"Lilly",
   "job_title":"Software Engineer",
   "id":"482",
   "createdAt":"2019-05-12T15:09:13.140Z"
}

DELETE Request

The DELETE request looks very similar to the POST request except body is not required:

const options = {
    method: 'DELETE',
    headers: {
        'Content-Type': 'application/json'
    }
}

fetch('https://reqres.in/api/users/2', options)
    .then(res => {
        if (res.ok) {
            return Promise.resolve('User deleted.');
        } else {
            return Promise.reject('An error occurred.');
        }
    })
    .then(res => console.log(res));

Error Handling

Since fetch() method returns a promise, error handling is easy. We can use the catch() method of the promise to intercept any error that is thrown during the execution of the request. However, no error will be thrown if the request hits the server and comes back, regardless of what response was returned by the server. The promise returned by the fetch() doesn't reject on HTTP errors even if the HTTP response code is 404 or 500.

Fortunately, you can use the ok property of response object to check whether the request was successful or not:

fetch('https://reqres.in/api/users/22') // 404 Error
    .then(res => {
        if (res.ok) {
            return res.json();
        } else {
            return Promise.reject(res.status);
        }
    })
    .then(res => console.log(res))
    .catch(err => console.log(`Error with message: ${err}`));

Response Object

The response object returned by the fetch() method contains the information about the request and the response of the asynchronous call including headers, status code, and status message:

fetch('https://reqres.in/api/users')
    .then(res => {
        console.log(res.headers.get('content-type'));
        console.log(res.headers.get('expires'));

        console.log(res.status);
        console.log(res.ok); // shorthand for `status` between 200 and 299 
        console.log(res.statusText);
        console.log(res.redirected);
        console.log(res.type);
        console.log(res.url);
    });

You can several options to access the response body of the Fetch API:

  • json() returns the body as a JSON object
  • text() returns the body as s string
  • blob() returns the body as a Blob object
  • formData() returns the body as a FormData object
  • arrayBuffer() returns the body as an arrayBuffer object

All these methods return a promise. Here is an example of text() method:

fetch('https://reqres.in/api/unknown/2')
    .then(res => res.text())
    .then(res => console.log(res));

The output of the above network call will be a JSON string:

'{"data":{"id":2,"name":"fuchsia rose","year":2001,"color":"#C74375","pantone_value":"17-2031"}}'

Fetch & Async/await

Since Fetch is a promise-based API, we can go one step further and use the latest ES2017 async/await syntax to make our code even simpler and synchronous-looking:

const fetchUsers = async () => {
    try {
        const res = await fetch('https://reqres.in/api/users');
        if (!res.ok) {
            throw new Error(res.status);
        }
        const data = await res.json();
        console.log(data);
    } catch (error) {
        console.log(error);
    }
}

fetchUsers();

Conclusion

That's all folks for using JavaScript Fetch API. It is a huge improvement over XMLHttpRequest with an easy-to-use interface and works great for fetching resources (even across the network). The Fetch API is supported by all modern browsers, so there is no need to use any polyfill unless you want to support IE.

Like this article? Follow @attacomsian on Twitter. You can also follow me on LinkedIn and DEV.

🙌 Join the weekly newsletter and get the latest tutorials from my blog and around the internet.
No spam, ever. You can unsubscribe anytime.

Need help to start a new Spring Boot or MEAN stack project? I am available for contract work. Hire me to accomplish your business goals with engineering and design. Let’s talk about your project: hi@attacomsian.com.