JavaScript - Fetch & AbortController

Overview

Estimated time: 35–45 minutes

Learn how to use fetch for HTTP requests, process JSON and text responses, detect and handle errors, set headers and bodies, and cancel requests with AbortController or timeouts.

Learning Objectives

  • Perform GET and POST requests with fetch and parse JSON safely.
  • Check response.ok and throw descriptive errors on failure.
  • Pass headers and JSON bodies correctly.
  • Cancel or timeout requests using AbortController and Promise.race patterns.

Prerequisites

GET JSON and error handling

async function getJSON(url) {
  const res = await fetch(url);
  if (!res.ok) {
    throw new Error(`HTTP ${res.status} ${res.statusText}`);
  }
  return res.json();
}

// Usage
// const data = await getJSON('https://example.com/api');

POST JSON

async function postJSON(url, data) {
  const res = await fetch(url, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(data),
  });
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  return res.json();
}

Abort and timeouts

// Abort with AbortController
async function fetchWithAbort(url, ms = 3000) {
  const ctrl = new AbortController();
  const t = setTimeout(() => ctrl.abort(), ms);
  try {
    const res = await fetch(url, { signal: ctrl.signal });
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    return await res.text();
  } finally {
    clearTimeout(t);
  }
}

// If supported, AbortSignal.timeout simplifies this:
// const res = await fetch(url, { signal: AbortSignal.timeout(3000) });

Retry with backoff (pattern)

async function retry(fn, { attempts = 3, baseMs = 200 } = {}) {
  let lastErr;
  for (let i = 0; i < attempts; i++) {
    try { return await fn(); }
    catch (e) {
      lastErr = e;
      if (i < attempts - 1) await new Promise(r => setTimeout(r, baseMs * 2**i));
    }
  }
  throw lastErr;
}

// Usage:
// const json = await retry(() => getJSON('https://example.com/api'));

Common Pitfalls

  • CORS: Requests to other origins may be blocked unless the server allows it.
  • Body usage: Response bodies are streams; you can only read once (e.g., await res.json() then it's consumed).
  • Content-Type: Set Content-Type: application/json when sending JSON; otherwise servers may not parse it.
  • Error detection: fetch only rejects on network errors. Use res.ok or status checks to treat HTTP errors as failures.

Try it

Run to GET JSON, then attempt an aborting fetch with a short timeout. If network is blocked, you'll see error logs.