Skip to content

Intermediate · Asynchronous JavaScript · topic 41 of 61 · 30 min

fetch: talking to servers

Explanation

fetch(url, options) sends an HTTP request and returns a promise for a Response. Check response.ok, because fetch does NOT reject on 404 or 500, only on network failure. Read the body with response.json(), send JSON with method, headers and body, and cancel slow requests with an AbortController.

HTTP in one paragraph

HTTP is the language browsers and servers use. A client sends a request: a method (GET to read, POST to create, PUT/PATCH to update, DELETE to remove), a URL, headers (extra information like Content-Type) and sometimes a body (the data). The server sends back a response: a status code (200 OK, 201 Created, 400 Bad Request, 401 Unauthorized, 404 Not Found, 500 Server Error), headers and a body, very often JSON.

A GET request

javascript
async function getTrain(number) {
  const response = await fetch(`https://api.example.com/trains/${number}`);
  if (!response.ok) {
    throw new Error(`Request failed: ${response.status}`);
  }
  return response.json();     // also a promise: reading the body takes time
}
fetch is built into browsers and into Node.js 18 and later.
Careful: fetch only rejects when the request could not be made at all (no internet, DNS failure, CORS block). A 404 or 500 response is a SUCCESSFUL fetch with response.ok === false. If you do not check ok, your code will try to use an error page as data.

The Response object

Property / methodGives
response.oktrue for status 200–299
response.statusthe number, e.g. 404
response.headers.get('content-type')a header value
await response.json()the body parsed as JSON
await response.text()the body as text

The body can be read only once. Calling json() and then text() on the same response throws "body used already".

Sending JSON with POST

javascript
async function createOrder(order) {
  const response = await fetch('https://api.example.com/orders', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(order),
  });
  if (!response.ok) throw new Error(`Could not create order: ${response.status}`);
  return response.json();  // e.g. { id: 'OD1023', status: 'PLACED' }
}

Timeouts and cancelling with AbortController

javascript
async function fetchWithTimeout(url, ms) {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), ms);
  try {
    const response = await fetch(url, { signal: controller.signal });
    return await response.json();
  } finally {
    clearTimeout(timer);
  }
}
// Newer engines also offer the shortcut fetch(url, { signal: AbortSignal.timeout(5000) }).

Aborting really stops the request (unlike Promise.race, which only stops waiting). The fetch promise then rejects with an error named AbortError.

A complete pattern

  1. Show a loading state.
  2. await fetch inside try.
  3. If !response.ok, throw an error with the status.
  4. Parse JSON and check the shape you expect.
  5. catch: show a friendly message and log the details.
  6. finally: hide the loading state.

CORS in one line

A web page can only read responses from another domain if that server allows it with CORS headers (Access-Control-Allow-Origin). A CORS error is fixed on the SERVER, not in your fetch call. Node.js scripts are not limited by CORS.

Think of it like this: fetch is like sending a courier. If the courier comes back saying "the address was a closed shop" (404), the delivery trip itself worked; you must read the note (response.ok) to know your parcel was not delivered. The courier only fails completely if the road was closed (network error).
Tip: Wrap fetch in one small helper (like getJson) that checks ok, parses JSON and throws good errors. Then the rest of your app never repeats that code. In tests, pass a fake fetch function to that helper.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

fetch: talking to servers · 1/7

fetch

In this lesson you will talk to servers with fetch.

Prefer a video by a teacher? Find free videos on “javascript fetch api tutorial async await post json” on YouTube ↗

In real life: PNR status with a fake server

Tests and demos should not call real servers. A booking app's PNR helper accepts a fetch function as a parameter, so tests can pass a fake one that returns ready-made responses, including a 404 for an unknown PNR.

javascript
const fakeFetch = async (url) => url.endsWith('/4521')
  ? { ok: true, status: 200, json: async () => ({ pnr: '4521', status: 'CNF' }) }
  : { ok: false, status: 404, json: async () => ({ error: 'Not found' }) };

async function getPnr(pnr, fetchFn = fetch) {
  const res = await fetchFn(`https://api.example.com/pnr/${pnr}`);
  if (!res.ok) throw new Error(`PNR lookup failed (${res.status})`);
  return res.json();
}
getPnr('4521', fakeFetch).then(console.log);                 // { pnr: '4521', status: 'CNF' }
getPnr('9999', fakeFetch).catch((e) => console.log(e.message)); // PNR lookup failed (404)

Practice (0/3 done)

Task 1 · Code · Javascript

Write getJson(url, fetchFn) that awaits fetchFn(url), throws an Error "HTTP <status>" when response.ok is false, and otherwise returns await response.json(). A fake fetch is provided in the tests.

Task 2 · Code · Javascript

Write postJson(url, data, fetchFn) that sends a POST with header Content-Type: application/json and the body JSON.stringify(data), and returns the parsed response JSON. The fake fetch echoes what it received.

Task 3 · Code · Javascript

Write fetchWithTimeout(url, ms, fetchFn) that creates an AbortController, aborts it after ms, passes { signal } to fetchFn, and returns the JSON. The fake slow fetch rejects with an AbortError when the signal aborts.

Quick quiz

Question 1

1.A server replies 404 Not Found. What does the fetch promise do?

Question 2

2.Which status code means "created"?

Question 3

3.What must the body of a JSON POST be?

Question 4

4.How do you really cancel a fetch?

Question 5

5.Where is a CORS error fixed?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

A server replies 404 Not Found. What does the fetch promise do?

Answer: Fulfils with a Response whose ok is false

fetch only rejects on network failure.

Which status code means "created"?

Answer: 201

201 Created is the usual answer to a successful POST.

What must the body of a JSON POST be?

Answer: A string made with JSON.stringify

fetch sends text; stringify the object.

How do you really cancel a fetch?

Answer: AbortController with signal

An aborted signal stops the request and rejects with AbortError.

Where is a CORS error fixed?

Answer: On the server, with the right headers

The server must allow the page's origin.

The server answers your fetch with 404 Not Found. What does fetch do?Related

Answer: It fulfils with a response whose ok is false

fetch only rejects when the request fails completely (for example no network). A 404 is a real answer, so you must check response.ok.

From fetch and APIs

The server answers 404 Not Found. What does await fetch(url) do?Related

Answer: It resolves to a Response with status 404 and res.ok false

fetch only rejects when no answer arrives. A 404 is an answer, so you must check res.ok or res.status yourself.

From Calling the API with fetch

Which part of an HTTP request carries the JSON data you send?Related

Answer: The body

The body comes after the headers and the empty line, and holds the data.

From HTTP requests, responses and methods

How do you cancel the notes request when the user leaves the page in React?Related

Answer: Create an AbortController, pass its signal to fetch, and call abort() in the useEffect cleanup

The cleanup runs when the component leaves the screen; abort() cancels the fetch, which rejects with an AbortError you can ignore.

From Loading, errors and slow networks

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. This is the last topic of “Asynchronous JavaScript”. The module exam comes next.
  2. Module 8 exam: Asynchronous JavaScript · 12 questions · 25 min, without help. 6 topics of this module still to finish first.
  3. Then the next module, TypeScript basics. 61 topics left in the course before the final exam.

← Previous: The event loop