⚡IntermediateBrowser JavaScript

Fetch API

Make HTTP requests from the browser using the Fetch API GET, POST, handling responses, errors, and request options.

Overview

The Fetch API is the modern, Promise-based way to make HTTP requests in JavaScript. It replaced the older XMLHttpRequest (XHR) with a cleaner, more readable interface. fetch() returns a Promise that resolves to a Response object, from which you can extract the data in various formats: JSON, text, Blob, FormData. One important quirk: fetch() only rejects its Promise on network failures (no connection, DNS error). HTTP error responses like 404 or 500 do not reject the Promise resolves with a Response where response.ok is false. You must check this yourself and throw if needed. Fetch supports full control over requests: HTTP method, headers, request body, credentials, CORS mode, and more via the options object. Combined with async/await, fetch-based code reads almost like synchronous file I/O, making it one of the most pleasant async patterns in the language.

Key Points

  • •fetch(url) returns a Promise that resolves to a Response object
  • •response.ok is true for 2xx status codes always check it for HTTP errors
  • •response.json() parses the body as JSON and returns a Promise
  • •response.text() returns the raw body as a string Promise
  • •Pass options as the second argument: { method, headers, body } for POST/PUT/DELETE
  • •JSON body must be stringified: body: JSON.stringify(data) with Content-Type: application/json
  • •fetch() never rejects on HTTP errors (4xx, 5xx) only on network failure
  • •Use AbortController to cancel in-flight fetch requests

Examples

Basic GET request with async/await

async function getPost(id) {
  const response = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`);

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

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

async function run() {
  try {
    const post = await getPost(1);
    console.log("Title:", post.title);
    console.log("Body:", post.body.slice(0, 50));
  } catch (err) {
    console.error("Failed:", err.message);
  }
}

run();

POST request with JSON body

async function createPost(title, body, userId) {
  const response = await fetch("https://jsonplaceholder.typicode.com/posts", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": "Bearer my-token",
    },
    body: JSON.stringify({ title, body, userId }),
  });

  if (!response.ok) {
    const errorText = await response.text();
    throw new Error(`${response.status}: ${errorText}`);
  }

  return response.json(); // returns Promise
}

async function run() {
  try {
    const newPost = await createPost("My Title", "Post content here", 1);
    console.log("Created post with id:", newPost.id);
  } catch (err) {
    console.error(err.message);
  }
}

run();

JSONPlaceholder is a free fake REST API useful for practicing fetch.

Aborting a fetch request and parallel requests

// Aborting a slow request
async function fetchWithTimeout(url, ms) {
  const controller = new AbortController();
  const timeoutId = setTimeout(() => controller.abort(), ms);

  try {
    const response = await fetch(url, { signal: controller.signal });
    clearTimeout(timeoutId);
    return response.json();
  } catch (err) {
    if (err.name === "AbortError") {
      throw new Error(`Request timed out after ${ms}ms`);
    }
    throw err;
  }
}

// Parallel requests with Promise.all
async function getMultiple(ids) {
  const promises = ids.map(id =>
    fetch(`https://jsonplaceholder.typicode.com/users/${id}`)
      .then(r => r.ok ? r.json() : null)
  );

  const users = await Promise.all(promises);
  return users.filter(Boolean);
}

getMultiple([1, 2, 3]).then(users =>
  users.forEach(u => console.log(u.name))
);

Common Mistakes

  • Not checking response.ok fetch resolves even for 404/500 responses, silently passing bad data to your app
  • Forgetting to set Content-Type: application/json when sending a JSON body the server may not parse it correctly
  • Trying to call response.json() twice Response bodies can only be consumed once
  • Not using AbortController for long-running requests they continue in the background even after component unmounts

Note

Create a small wrapper function (often called apiFetch or httpClient) that handles response.ok checking, JSON parsing, and error throwing in one place. All your feature code then calls this wrapper instead of raw fetch, keeping error handling consistent.

Exercises

Write JavaScript code to solve each exercise. Run it and check your output.

Loading editor…

Completed: 0 / 5 exercises

Knowledge Check

Question 1 of 4

When does fetch() reject its returned Promise?