JavaScript fetch

Talk to a Server

JavaScript fetch

`fetch` makes HTTP requests. Returns a Promise that resolves to a Response — and there's one gotcha around error checking.

5 min read Level 3/5 #fetch#http#ajax
What you'll learn
  • Make a GET request with `fetch`
  • Parse a JSON response
  • Check `response.ok` for HTTP errors
  • Make a POST request with a JSON body

fetch is the modern way to make HTTP requests from JavaScript. It works in browsers and modern Node.js. It returns a Promise that resolves to a Response object.

A Basic GET

const response = await fetch("https://api.example.com/users/1");
const user = await response.json();
console.log(user);

Two awaits — one for the response headers, one for the response body. .json() reads the body and parses it as JSON.

The response.ok Gotcha

fetch only rejects on network failure — DNS failures, dropped connections, CORS issues. It does NOT reject on HTTP error statuses like 404 or 500.

const response = await fetch("/api/users/1");

if (!response.ok) {
  throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}

const user = await response.json();

POST With a JSON Body

To send data, pass an options object as the second argument.

const response = await fetch("/api/users", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ name: "Ada", role: "admin" }),
});

if (!response.ok) throw new Error(`HTTP ${response.status}`);
const created = await response.json();
console.log(created);

Three things to remember when posting JSON:

  1. method: "POST".
  2. Content-Type: application/json header.
  3. JSON.stringify(...) the body — fetch sends strings, not objects.

Other Response Bodies

The Response object has several body readers:

MethodReturns
response.json()Parsed JSON
response.text()Raw text
response.blob()Binary data (Blob)
response.arrayBuffer()Binary as ArrayBuffer
response.formData()FormData (for forms)

You can only consume the body once — it’s a stream. To read it both as text and as JSON, clone first with response.clone().

Aborting a Request

If a request might run long, use AbortController to cancel it.

const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5000);

try {
  const response = await fetch("/slow", { signal: controller.signal });
  const data = await response.json();
  console.log(data);
} catch (e) {
  if (e.name === "AbortError") console.log("timed out");
  else throw e;
} finally {
  clearTimeout(timeout);
}

This is the standard pattern for timeouts on fetch.

A Full Wrapper

A reusable helper that handles the common cases:

async function api(path, options = {}) {
  const response = await fetch(path, {
    headers: { "Content-Type": "application/json" },
    ...options,
    body: options.body ? JSON.stringify(options.body) : undefined,
  });

  if (!response.ok) {
    throw new Error(`HTTP ${response.status}: ${response.statusText}`);
  }

  return response.json();
}

// Use:
const user = await api("/api/users/1");
const created = await api("/api/users", {
  method: "POST",
  body: { name: "Ada" },
});

Most real apps end up with something like this wrapping fetch.

Try It Yourself

Exercise

Fetch With Error Handling

Difficulty 2/5~6 min
Write an async function `getJSON(url)` that: 1. calls `fetch(url)` 2. throws an `Error` with the status if `res.ok` is `false` 3. returns the parsed JSON body if it's ok This is the helper most apps wish `fetch` actually behaved like.
solution.js
async function getJSON(url) {
  // your code
}
0tests will run
✅ Show solution
async function getJSON(url) {
  const res = await fetch(url);
  if (!res.ok) {
    throw new Error(`HTTP ${res.status}`);
  }
  return res.json();
}

Up Next

What to do when something goes wrong — error handling, try/catch/finally.

JavaScript Error Handling →