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 AbortController to cancel stale requests.
  • Use AbortSignal.timeout() instead of hand-rolling a timeout with setTimeout.
Back to Home