Course topics

By WebNest Studio Engineering Team

JavaScript Tutorial

The Fetch API

The Fetch API is the modern, Promise-based way to make HTTP requests from JavaScript — replacing the older, callback-based XMLHttpRequest. A single call to fetch(url) returns a Promise that resolves to a Response object once the server has responded with headers, even before the body has fully arrived.

Because fetch() returns a Promise, it pairs naturally with .then() chains or async/await, and reading the actual response body is itself an additional asynchronous step.

Why it matters

fetch is how a web page talks to a server: loading data, submitting forms, calling APIs. Its biggest surprise is that an error response such as 404 or 500 does not reject the promise, so code that skips the status check happily processes an error page as if it were data.

A Basic GET Request

fetch(url) defaults to a GET request. The resolved Response object doesn't contain the parsed data directly — you call a method like response.json() (or .text(), .blob()) to read and parse the body, and that method also returns a Promise, since reading the full body can take time.

Checking response.ok

A critical detail: fetch()'s Promise only rejects on network failure (like no internet connection or a DNS error) — it does NOT reject for HTTP error statuses like 404 or 500. The response still resolves successfully, just with response.ok === false and response.status set to the error code. Correct code always checks response.ok before treating the response as usable data, and throws manually if it isn't.

Making a POST Request

To send data, pass a second options object specifying the method, headers, and a body. JSON payloads need both JSON.stringify() on the body and a Content-Type: application/json header so the server parses it correctly.

  • method — 'GET' (default), 'POST', 'PUT', 'DELETE', etc.
  • headers — an object of request headers, e.g. { 'Content-Type': 'application/json' }.
  • body — the request payload, typically JSON.stringify(data) for JSON APIs.

Examples

A GET request with proper response.ok checking

JavaScript
async function getUser(id) {
  const response = await fetch(`https://api.example.com/users/${id}`);

  if (!response.ok) {
    throw new Error(`Request failed with status ${response.status}`);
  }

  const user = await response.json();
  return user;
}

getUser(42)
  .then((user) => console.log('Fetched:', user.name))
  .catch((err) => console.error('Error:', err.message));

// If the server returns 404, response.ok is false and the manual
// throw runs, producing: "Error: Request failed with status 404"
Output
Fetched: Asha Verma

A POST request sending a JSON body

JavaScript
async function createUser(newUser) {
  const response = await fetch('https://api.example.com/users', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(newUser),
  });

  if (!response.ok) {
    throw new Error(`Create failed with status ${response.status}`);
  }

  return response.json();
}

createUser({ name: 'Rahul', age: 31 }).then((created) =>
  console.log('Created user with id:', created.id)
);
Output
Created user with id: 108

Common Mistakes

  • Assuming fetch() rejects on a 404 or 500 response — it only rejects on network failure; you must manually check response.ok or response.status.
  • Forgetting to await (or chain .then() on) response.json(), which is itself asynchronous and returns a Promise, not the parsed data directly.
  • Sending a JSON body without setting the Content-Type: application/json header, causing the server to misinterpret or reject the payload.
  • Forgetting JSON.stringify() on the request body, sending "[object Object]" as literal text instead of valid JSON.

Exercise

fakeFetch behaves like fetch but needs no network: it resolves with a response object that has ok, status and a json() method. Write getJson, which returns the parsed body when the response is ok and otherwise throws an error whose message is HTTP followed by the status.

Expected output: Asha then HTTP 404

JavaScript · starter code
function fakeFetch(url) {
  const found = url === "/users/1";
  return Promise.resolve({
    ok: found,
    status: found ? 200 : 404,
    json: () => Promise.resolve(found ? { name: "Asha" } : {}),
  });
}

async function getJson(url) {
  // TODO: call fakeFetch, throw "HTTP <status>" if the response is not ok,
  //       otherwise return the parsed JSON
}

async function main() {
  const user = await getJson("/users/1");
  console.log(user.name);

  try {
    await getJson("/users/99");
  } catch (error) {
    console.log(error.message);
  }
}

main();
Show hints
  • Test response.ok before reading the body.
  • response.json() returns a promise, so it must be awaited too.
Show solution
JavaScript · solution
function fakeFetch(url) {
  const found = url === "/users/1";
  return Promise.resolve({
    ok: found,
    status: found ? 200 : 404,
    json: () => Promise.resolve(found ? { name: "Asha" } : {}),
  });
}

async function getJson(url) {
  const response = await fakeFetch(url);
  if (!response.ok) {
    throw new Error("HTTP " + response.status);
  }
  return await response.json();
}

async function main() {
  const user = await getJson("/users/1");
  console.log(user.name);

  try {
    await getJson("/users/99");
  } catch (error) {
    console.log(error.message);
  }
}

main();

Quick quiz

1. The server responds with status 404. What happens to the promise returned by fetch?

  • A. It rejects
  • B. It fulfils with a response whose ok property is false
  • C. It stays pending
  • D. It retries the request
Show answer

B. fetch rejects only for network failures. The status must be checked by the code.

2. What does response.json() return?

  • A. A JSON string
  • B. The parsed object directly
  • C. A promise that resolves with the parsed body
  • D. The status code
Show answer

C. Reading the body is asynchronous, so it needs its own await.

3. How is JSON sent in the body of a POST request with fetch?

  • A. body: data
  • B. data: JSON.parse(data)
  • C. json: data
  • D. body: JSON.stringify(data), with a Content-Type header of application/json
Show answer

D. The body must be a string, and the header tells the server how to read it.

Interview questions

How do you handle errors correctly with fetch?

Show answer

Two kinds of failure need handling. A network failure, such as no connection or a blocked request, rejects the promise and is caught with try and catch. An HTTP error status, such as 404 or 500, does not reject: the promise fulfils, so the code must check response.ok or response.status and throw an error itself. Only after that check should the body be parsed.

What is CORS?

Show answer

Cross-Origin Resource Sharing is the browser mechanism that controls requests from a page to a different origin. By default the browser blocks a script from reading such a response. The server allows it by sending headers such as Access-Control-Allow-Origin naming the permitted origins. For some requests the browser first sends an OPTIONS preflight to ask permission. CORS is configured on the server and cannot be bypassed from front-end code.

Key Points to Remember

  • fetch(url) returns a Promise resolving to a Response once headers arrive; reading the body (.json(), .text()) is a separate async step.
  • fetch()'s Promise only rejects on network errors — always check response.ok/response.status for HTTP-level failures.
  • POST/PUT requests need a method, a Content-Type header, and a JSON.stringify()-encoded body for JSON APIs.
  • fetch pairs naturally with async/await for readable request/response handling.

Practice in the playground

Write and run your own code for this topic, or start from the exercise or an example. Shows a live preview of your HTML, CSS and JavaScript. Your code for each topic is kept in this browser.

Run the examples

Change an input, predict the result, then compare it with the output. Explain why the result changes.

WebNest Studio

Written by

WebNest Studio Engineering Team

Practical lessons from developers working with Java, Python, React, databases and production systems.

Led by Vansh Duggal, Founder, WebNest Studio · Full-stack & software engineering