ToolPopToolPop
JavaScript · Lesson 6 of 18

fetch and APIs, talking to a real backend

11 min readUpdated 24 Jun 2026

fetch is how JavaScript talks to APIs in 2026. It is built into every browser, every Node version since 18, every modern runtime. No axios required unless you really want it.

It is also the source of one of the most common bugs in early JS code. We will cover that too.

Diagram
rendering diagram...
fetch: the two-await pattern, and what 'failure' actually means

The simplest GET

js
const res = await fetch('https://api.example.com/orders');
const data = await res.json();

Two awaits. The first awaits the HTTP response. The second awaits the body being parsed as JSON. res.json() is itself async because the body may still be streaming.

Need text? Use res.text(). Need a binary blob? Use res.blob().

POST with a JSON body

js
const res = await fetch('https://api.example.com/orders', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ restaurant: 'MTR', total: 320 }),
});
  • method defaults to GET. Set POST, PUT, PATCH, DELETE as needed.
  • headers is a plain object. The Content-Type header tells the server what shape your body is in.
  • body must be a string for JSON. JSON.stringify is mandatory. Forget it and your server will get [object Object].

The fetch gotcha that catches everyone

fetch does not reject on a 404 or 500 response. It only rejects on a network failure (DNS, connection refused, CORS block). A 500 from the server is still a "successful" response in fetch's view.

js
const res = await fetch(url);
if (!res.ok) {
  throw new Error(`API failed: ${res.status}`);
}
const data = await res.json();

Check res.ok (true for 200-299) or res.status explicitly. Every. Single. Time.

If you skip the res.ok check, your code will silently treat error pages as data and fail in a confusing way later.

CORS, the browser bouncer

CORS (Cross-Origin Resource Sharing) is a browser security feature. By default, your JavaScript at myapp.com cannot read responses from api.someoneelse.com. The browser blocks it unless that server explicitly allows you with an Access-Control-Allow-Origin header.

This is a server-side fix, not a client one. If you control the API, add the header. If you do not, route the request through your own backend. There is no magic fetch flag that disables CORS in a browser, and that is by design.

AbortController for cancellable requests

User typed in a search box, then typed again. You want to cancel the first request. That is what AbortController is for.

js
const controller = new AbortController();
const res = await fetch('/api/search?q=biryani', { signal: controller.signal });
 
// later, somewhere:
controller.abort();

The aborted fetch rejects with an AbortError. Catch it and move on. Almost every real search UI uses this pattern.

Quick recap

  • fetch(url, options), then await res.json() for the body.
  • Always check res.ok. Fetch does not throw on 4xx or 5xx.
  • CORS is a server-side header. Cannot be bypassed in the browser.
  • Use AbortController to cancel stale requests.

Next lesson: the DOM, which is where all this fetched data finally meets the user.

Free tools you can use while you learn

Chai0/1 done

Watching quietly. Tap me if you want a tip.

JS Playground
console
// hit run to see output

Try this (0 of 1 done)

  1. 1

    Use async/await to fetch from https://api.github.com/zen and log it.

    show answer
    (async () => {
      const r = await fetch('https://api.github.com/zen');
      const text = await r.text();
      console.log(text);
    })();