Making HTTP requests with fetch

JavaScript · Practical · 12 min

Talking to servers is the core of most web apps, and fetch is the standard API in browsers and Node.js.

fetch(url, options) makes an HTTP request and returns a promise for a Response. It's built into browsers and into Node.js 18+.

const res = await fetch("https://api.example.com/users/1");
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const user = await res.json();

The crucial detail: fetch only rejects on network failure (offline, DNS error, CORS refusal, abort). An HTTP error like 404 or 500 still fulfils — you must check res.ok (status 200–299) or res.status yourself.

Reading the body is a second async step: res.json(), res.text(), res.blob() or res.arrayBuffer(). A body can only be read once.

To send data, set the method, headers and body:

await fetch("/api/todos", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ title: "Write docs" }),
});

For form uploads, pass a FormData as the body and let fetch set the content type. Query strings are easiest to build with new URL(base) and url.searchParams.set(...), which handles encoding.

Cancellation uses an AbortSignal: pass { signal: controller.signal } and call controller.abort(), or use AbortSignal.timeout(5000) for a timeout. The promise then rejects with an AbortError or TimeoutError.

In browsers, requests to another origin are subject to CORS: the server must allow your origin, otherwise the browser blocks the response and fetch rejects with a generic TypeError. Cookies are sent for same-origin requests by default; cross-origin needs credentials: "include".

Example

// Runs in Node.js 18+ or a browser module
async function getJSON(url, { timeoutMs = 5000 } = {}) {
  const res = await fetch(url, {
    headers: { Accept: "application/json" },
    signal: AbortSignal.timeout(timeoutMs),
  });
  if (!res.ok) {
    throw new Error(`Request failed: ${res.status} ${res.statusText}`);
  }
  return res.json();
}

const url = new URL("https://jsonplaceholder.typicode.com/posts");
url.searchParams.set("userId", "1");

try {
  const posts = await getJSON(url);
  console.log(posts.length, posts[0].title);
} catch (err) {
  if (err.name === "TimeoutError") console.error("Too slow");
  else console.error(err.message);
}

Remember

  • fetch rejects only on network errors — always check res.ok
  • Reading the body (res.json()) is a separate await
  • Send JSON with method, Content-Type header and JSON.stringify body; cancel with AbortSignal

Watch out for

  • Assuming a 404 or 500 response makes fetch throw.

Check yourself

  1. The server responds 404 Not Found. What does await fetch(url) do?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch