Skip to content

Fetch

Data-fetching is a huge part of most React applications. We often have a bunch of data that lives in a database somewhere, and we need to retrieve or update it from our React application. One of the most straightforward ways to make a network request is to use Fetch.

Fetch is a built-in part of the web platform, a modern replacement for the clunky XMLHttpRequest. It allows us to make requests over the network.

Retrieving data

Let's look at a “hello world” example using fetch:

async function getData() {
const response = await fetch('/api/data');
const json = await response.json();
// `json` is the raw data returned from the API
}

We're calling fetch with the API endpoint, /api/data. Fetch is promise-based, and so this expression starts the request and produces a promise.

We wait for this response to resolve with await. response is an object that describes the response. It includes information about headers, the status code, and other networky stuff like that.

In order to get the raw data, we call response.json(), which provides whatever the server responded with.

Perhaps the most confusing thing about fetch is that response.json() also produces a promise. Why do we need to await that call? We already have the response!

Well, here's the thing: it's possible for servers to "stream" the response. If the JSON payload is large enough, it might come in multiple batches. And so even though we've completed our round trip to the API endpoint, we might not have the full response yet.

(In practice, response.json() usually resolves immediately, but we nevertheless need to await it.)

Request options

By default, fetch will send a GET request. But what if we want to send a POST, or any of the other valid HTTP methods?

We can do this by specifying a second argument:

async function submitData() {
const response = await fetch('/api/data', {
method: 'POST',
});
const json = await response.json();
// `json` is the raw data returned from the API
}

This second argument allows us to specify all sorts of useful options. For example, we can specify headers like this:

async function submitData() {
const response = await fetch('/api/data', {
method: 'POST',
headers: {
'Authorization': 'bearer abc123',
},
});
const json = await response.json();
// `json` is the raw data returned from the API
}

Query and body params

When making network requests, we often need to send along some data. This is typically done with query params (for GET requests) or body params (for POST/PUT/PATCH/DELETE requests).

For query params, we can specify them right in the URL:

async function search({ name, city }) {
const url = `/api/search?name=${name}&city=${city}`;
const response = await fetch(url);
const json = await response.json();
}

Query parameters always start with a question mark (?), and key/value pairs are separated with an ampersand (&).

For body params, we can include them in the fetch options:

async function login({ username, password }) {
const response = await fetch(`/api/login`, {
method: 'POST',
body: JSON.stringify({
username,
password,
}),
});
const json = await response.json();
}

We need to stringify the data first, because we're only able to send strings over the network, not objects or arrays. JSON.stringify turns it into a string, and the back-end server can use JSON.parse to transform it back into an object.