AI Workshop: learn to build apps with AI →
Networking: The Fetch API

Join the AI Workshop and learn to build real-world apps with AI. A hands-on, practical program to level up your skills.


Introduction to the Fetch API

Since IE5 (1998), browsers have supported asynchronous network calls via XMLHttpRequest (XHR). Gmail and other rich apps later made heavy use of it, and the approach became known as AJAX.

Working directly with XMLHttpRequest was cumbersome and was usually abstracted by libraries; jQuery, for example, provided helpers:

  • jQuery.ajax()
  • jQuery.get()
  • jQuery.post()

The Fetch API is the modern, standards-based way to make asynchronous network requests; it is built on Promises.

Fetch is well supported in major browsers except IE.

The browser support for the Fetch API

A polyfill lets you use fetch in older browsers.

Using Fetch

A simple GET request:

fetch('/file.json')

This sends an HTTP request for file.json on the same origin. The fetch function is available on the global window object.

To read the response body:

fetch('./file.json')
  .then((response) => response.json())
  .then((data) => console.log(data))

fetch() returns a promise. Chain then() to handle the response. The handler receives a Response object.

We’ll see this object in detail in the next section.

Catching errors

Because fetch() returns a promise, you can use catch() to handle errors from the request or from then() callbacks:

fetch('./file.json')
  .then((response) => {
    //...
  })
  .catch((err) => console.error(err))

Alternatively, handle errors in the first then:

fetch('./file.json')
  .then((response) => {
    if (!response.ok) {
      throw Error(response.statusText)
    }
    return response
  })
  .then((response) => {
    //...
  })

Response Object

The Response object returned by fetch() contains the response data and metadata.

Metadata

headers

The headers property gives you the HTTP response headers:

fetch('./file.json').then((response) => {
  console.log(response.headers.get('Content-Type'))
  console.log(response.headers.get('Date'))
})

Request headers for fetch

status

This property is an integer representing the HTTP status:

  • 101, 204, 205, and 304 indicate a null body
  • 200–299 indicate success
  • 301, 302, 303, 307, and 308 indicate redirects
fetch('./file.json').then((response) => console.log(response.status))

statusText

statusText is the status message (e.g. OK for success).

fetch('./file.json').then((response) => console.log(response.statusText))

url

url is the full URL of the resource that was fetched.

fetch('./file.json').then((response) => console.log(response.url))

Body content

A response has a body, accessible using several methods:

  • text() returns the body as a string
  • json() returns the body as a JSON-parsed object
  • 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 those methods return a promise. Examples:

fetch('./file.json')
  .then((response) => response.text())
  .then((body) => console.log(body))
fetch('./file.json')
  .then((response) => response.json())
  .then((body) => console.log(body))

Get JSON

The same can be written using the ES2017 async functions:

;(async () => {
  const response = await fetch('./file.json')
  const data = await response.json()
  console.log(data)
})()

Request Object

The Request object represents a resource request, and it’s usually created using the new Request() API.

Example:

const req = new Request('/api/todos')

The Request object offers several read-only properties to inspect the request details, including:

  • method: the request’s method (GET, POST, etc.)
  • url: the URL of the request.
  • headers: the associated Headers object of the request
  • referrer: the referrer of the request
  • cache: the cache mode of the request (e.g., default, reload, no-cache).

It also exposes methods such as json(), text(), and formData() to read the request body.

The full API can be found at https://developer.mozilla.org/docs/Web/API/Request

Request headers

Setting request headers is essential; fetch lets you do this with the Headers object:

const headers = new Headers()
headers.append('Content-Type', 'application/json')

or:

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

Pass a Request object to fetch() instead of a URL to attach headers. Instead of fetch('./file.json'), use:

const request = new Request('./file.json', {
  headers: new Headers({
    'Content-Type': 'application/json',
  }),
})
fetch(request)

You can also query the Headers object:

headers.has('Content-Type')
headers.get('Content-Type')

and we can delete a header that was previously set:

headers.delete('X-My-Custom-Header')

POST Requests

Fetch supports other HTTP methods (POST, PUT, DELETE, OPTIONS). Set the method in the options and pass headers and body as needed. Example POST:

const options = {
  method: 'post',
  headers: {
    'Content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
  },
  body: 'name=Flavio&test=1',
}

fetch(url, options).catch((err) => {
  console.error('Request failed', err)
})

How to cancel a fetch request

Initially there was no way to abort a fetch. AbortController and AbortSignal now allow this. Pass a signal in the fetch options:

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

fetch('./file.json', { signal })

Example: abort after 5 seconds:

setTimeout(() => controller.abort(), 5 * 1000)

If the fetch has already completed, calling abort() has no effect. When aborted, fetch rejects with a DOMException named AbortError:

fetch('./file.json', { signal })
  .then((response) => response.text())
  .then((text) => console.log(text))
  .catch((err) => {
    if (err.name === 'AbortError') {
      console.error('Fetch aborted')
    } else {
      console.error('Another error', err)
    }
  })

Lessons in this unit:

0: Introduction
1: ▶︎ The Fetch API
2: XMLHttpRequest
3: CORS
4: Streams API