How to use Fetch API to make HTTP requests in JavaScript

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 provide a powerful and flexible feature set to fetch resources from the server.

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

How to use Fetch API?

Using Fetch API is really simple. Just pass the URL, the path to the resource you want to fetch, to the 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 sends the response to then() when it is fulfilled. The catch() method intercepts errors if the request fails to complete due to network failure or other reasons.

Make GET request using Fetch API

By default, the Fetch API uses the GET method for asynchronous requests. Let's use the Reqres REST API to retrieve a list of users using a 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 the fetch() method returns a promise. The response returned by the promise is a stream object. When we call the json() method on the stream object, it returns another promise. The call to the json() method indicates that we are expecting a JSON response. For an XML response, you should use the text() method.

Make POST request using Fetch API

Just like Axios, Fetch also allows us 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 returns 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"
}

Make DELETE request using Fetch API

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 in Fetch API

Since the fetch() method returns a promise, error handling is easy. We can use the catch() method of the promise object to intercept any error thrown during the execution of the request.

However, no error will be thrown if the request hits the server and comes back, regardless of the server's response. The promise returned by fetch() does not reject HTTP errors, even if the HTTP response code is 404 or 500.

Fortunately, you can use the ok property of the 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}`))

Fetch API request headers

Request headers (like Accept, Content-Type, User-Agent, Referer, etc.) are essential for any HTTP request. The Fetch API's Headers object allows us to set, remove, or retrieve HTTP request headers.

We can create a header object using the Headers() constructor and then use the append, has, get, set, and delete methods to modify request headers:

// create an empty `Headers` object
const headers = new Headers()

// add headers
headers.append('Content-Type', 'text/plain')
headers.append('Accept', 'application/json')

// add custom headers
headers.append('X-AT-Platform', 'Desktop')
headers.append('X-AT-Source', 'Google Search')

// check if the header exists
headers.has('Accept') // true

// get headers
headers.get('Accept') // application/json
headers.get('X-AT-Source') // Google Search

// update header value
headers.set('Content-Type', 'application/json')

// remove headers
headers.delete('Content-Type')
headers.delete('X-AT-Platform')

We can also pass an array of arrays or an object literal to the constructor to create a header object:

// passing an object literal
const headers = new Headers({
  'Content-Type': 'application/json',
  Accept: 'application/json'
})

// OR

// passing an array of arrays
const headers = new Headers([
  ['Content-Type', 'application/json'],
  ['Accept', 'application/json']
])

To add headers to the request, create a Request instance and pass it to the fetch() method instead of the URL:

const request = new Request('https://reqres.in/api/users', {
  headers: headers
})

fetch(request)
  .then(res => res.json())
  .then(json => console.log(json))
  .catch(err => console.error('Error:', err))

Fetch API request object

The Request object represents a resource request and can be created by calling the Request() constructor:

const request = new Request('https://reqres.in/api/users')

The Request object also accepts a URL object:

const url = new URL('https://reqres.in/api/users')
const request = new Request(url)

By passing a Request object to fetch(), you can easily customize the request properties:

  • method — An HTTP method like GET, POST, PUT, DELETE, HEAD
  • url — The URL to the request, a string or a URL object
  • headers — a Headers object for request headers
  • referrer — referrer of the request (e.g., client)
  • mode — The mode for cross-origin requests (e.g., cors, no-cors, same-origin)
  • credentials — Should cookies and HTTP-Authorization headers go with the request? (e.g., include, omit, same-origin)
  • redirect — The redirect mode of the request (e.g., follow, error, manual)
  • integrity — The subresource integrity value of the request
  • cache — The cache mode of the request (e.g, default, reload, no-cache)

Let us create a Request object with some customized properties and body content to make a POST request:

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

const headers = new Headers({
  'Content-Type': 'application/json',
  Accept: 'application/json'
})

const request = new Request('https://reqres.in/api/users', {
  method: 'POST',
  headers: headers,
  redirect: 'follow',
  mode: 'cors',
  body: JSON.stringify(user)
})

fetch(request)
  .then(res => res.json())
  .then(json => console.log(json))
  .catch(err => console.error('Error:', err))

Only the first argument, the URL, is required. All these properties are read-only. You can not change their value once the request object is created. The Fetch API does not strictly require a Request object. The object literal, which we pass to the fetch() method, acts like a Request object:

fetch('https://reqres.in/api/users', {
  method: 'POST',
  headers: headers,
  redirect: 'follow',
  mode: 'cors',
  body: JSON.stringify(user)
})
  .then(res => res.json())
  .then(json => console.log(json))
  .catch(err => console.error('Error:', err))

Fetch API response object

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

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

  // HTTP response status code
  console.log(res.status)

  // shorthand for `status` between 200 and 299
  console.log(res.ok)

  // status message of the response e.g. `OK`
  console.log(res.statusText)

  // check if there was a redirect
  console.log(res.redirected)

  // get the response type (e.g., `basic`, `cors`)
  console.log(res.type)

  // the full path of the resource
  console.log(res.url)
})

The response body is accessible through the following methods:

  • json() returns the body as a JSON object
  • text() returns the body as a 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"}}'

Send cookies using Fetch API

When you use Fetch to get a resource, the request does not contain credentials such as cookies. If you want to send cookies, you have to explicitly enable credentials like the below:

fetch(url, {
  credentials: 'include'
})

Use async-await with Fetch API

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 introduction to JavaScript Fetch API. It is a significant improvement over XMLHttpRequest with a simple, elegant, and easy-to-use interface. Fetch works great for fetching network resources (even across the network inside the service workers).

The Fetch API is supported by all modern browsers, so there is no need to use any polyfill unless you want to support IE.

Read next: How to make HTTP requests using XHR in JavaScript.

✌️ Like this article? Follow me on Twitter and LinkedIn. You can also subscribe to RSS Feed.

You might also like...

Digital Ocean

The simplest cloud platform for developers & teams. Start with a $200 free credit.

Buy me a coffee ☕

If you enjoy reading my articles and want to help me out paying bills, please consider buying me a coffee ($5) or two ($10). I will be highly grateful to you ✌️

Enter the number of coffees below:

✨ Learn to build modern web applications using JavaScript and Spring Boot

I started this blog as a place to share everything I have learned in the last decade. I write about modern JavaScript, Node.js, Spring Boot, core Java, RESTful APIs, and all things web development.

The newsletter is sent every week and includes early access to clear, concise, and easy-to-follow tutorials, and other stuff I think you'd enjoy! No spam ever, unsubscribe at any time.