A Practical Guide to the Fetch API
by Kuligaposten 2026-08-23
Making requests, handling errors properly, and cancelling requests with AbortController.
A Practical Guide to the Fetch API
fetch() replaced XMLHttpRequest as the standard way to make HTTP requests in the browser. The basics are simple, but a few sharp edges trip people up — especially around error handling.
The basic request
const response = await fetch("/api/posts");
const posts = await response.json();
console.log(posts);
Fetch doesn't reject on HTTP errors
This is the most common gotcha: fetch() only rejects on a network failure (DNS error, no connection, CORS block). A 404 or 500 response is still a "successful" fetch as far as the promise is concerned. You have to check response.ok yourself.
async function getPost(slug) {
const response = await fetch(`/api/posts/${slug}`);
if (!response.ok) {
throw new Error(`Request failed: ${response.status}`);
}
return response.json();
}
Sending JSON
await fetch("/api/posts", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ title: "New Post", body: "Hello!" }),
});
Cancelling a request
Search-as-you-type is a classic case where you fire a request, then the user keeps typing and you no longer care about the response. AbortController lets you cancel an in-flight fetch:
let controller;
async function search(query) {
controller?.abort();
controller = new AbortController();
const response = await fetch(`/api/search?q=${query}`, {
signal: controller.signal,
});
return response.json();
}
If a new search() call comes in before the previous one finishes, the old request is aborted and its promise rejects with an AbortError — which you usually just want to ignore:
try {
const results = await search(query);
render(results);
} catch (err) {
if (err.name !== "AbortError") throw err;
}
Timeouts
Fetch has no built-in timeout option, but AbortSignal.timeout() covers the common case in one line:
const response = await fetch("/api/slow-endpoint", {
signal: AbortSignal.timeout(5000),
});
Summary
- Always check
response.ok— fetch won't do it for you. - Use
AbortControllerto cancel stale requests. - Use
AbortSignal.timeout()instead of hand-rolling a timeout withsetTimeout.