HVRDHVRD
JavaScript

Fetch API

Comprehensive guide to using the Fetch API in JavaScript for making HTTP requests, handling responses, and managing errors.

Fetch API

The Fetch API provides a modern, promise-based way to make HTTP requests in JavaScript. It simplifies the process of sending requests to and receiving responses from web servers compared to older technologies like XMLHttpRequest.


What is Fetch API?

The Fetch API is built into modern browsers and provides an interface for accessing and manipulating HTTP requests and responses.

fetch(url, options)
  • url: The resource URL.
  • options (optional): An object to customize the request (method, headers, body, etc.).

The fetch() function returns a Promise that resolves to a Response object.


Basic GET Request

A simple use case is fetching data from a public API.

fetch('https://api.example.com/data')
  .then(response => {
    if (!response.ok) {
      throw new Error(`HTTP error! Status: ${response.status}`);
    }
    return response.json();
  })
  .then(data => {
    console.log(data);
  })
  .catch(error => {
    console.error('Fetch error:', error);
  });

Explanation

  • fetch() initiates the HTTP request.
  • The first .then() checks if the response was successful (response.ok).
  • .json() parses the response body into a JavaScript object.
  • The final .catch() handles network errors or parsing issues.

Sending POST Requests

For sending data to the server, configure the method, headers, and body in the options object.

fetch('https://api.example.com/submit', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ name: 'John Doe', age: 30 }),
})
  .then(response => response.json())
  .then(data => console.log('Success:', data))
  .catch(error => console.error('Error:', error));

Key Points

  • Use method: 'POST' for POST requests.
  • headers specify the data format.
  • body carries the payload, usually stringified JSON.

Working with Response Headers

The Response object provides access to headers.

fetch('https://api.example.com/data')
  .then(response => {
    console.log('Content-Type:', response.headers.get('Content-Type'));
    return response.json();
  })
  .then(data => console.log(data));

Handling Errors

The Fetch API does not reject the Promise for HTTP error statuses (like 404 or 500). It only rejects on network errors.

fetch('https://api.example.com/invalid-endpoint')
  .then(response => {
    if (!response.ok) {
      throw new Error(`Server responded with ${response.status}`);
    }
    return response.json();
  })
  .catch(error => console.error('Fetch failed:', error));

Always check response.ok and response.status.


Aborting a Fetch Request

Use the AbortController to cancel fetch requests.

const controller = new AbortController();
const signal = controller.signal;

fetch('https://api.example.com/data', { signal })
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => {
    if (error.name === 'AbortError') {
      console.log('Fetch aborted');
    } else {
      console.error('Fetch error:', error);
    }
  });

// Abort after 3 seconds
setTimeout(() => controller.abort(), 3000);

Timeout Implementation

Fetch has no built-in timeout, so combine it with AbortController for timeouts.

function fetchWithTimeout(url, options, timeout = 5000) {
  const controller = new AbortController();
  const signal = controller.signal;

  const fetchPromise = fetch(url, { ...options, signal });

  const timeoutId = setTimeout(() => controller.abort(), timeout);

  return fetchPromise
    .finally(() => clearTimeout(timeoutId));
}

fetchWithTimeout('https://api.example.com/data', {}, 3000)
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => {
    if (error.name === 'AbortError') {
      console.log('Request timed out');
    } else {
      console.error('Fetch error:', error);
    }
  });

Streaming Response Body

Access the response body as a stream using response.body.

fetch('https://api.example.com/large-file')
  .then(response => {
    const reader = response.body.getReader();
    return new ReadableStream({
      start(controller) {
        function push() {
          reader.read().then(({ done, value }) => {
            if (done) {
              controller.close();
              return;
            }
            controller.enqueue(value);
            push();
          });
        }
        push();
      }
    });
  })
  .then(stream => {
    // Process the stream
    console.log('Stream is ready');
  });

Best Practices

  • Always handle response.ok to catch HTTP errors.
  • Use AbortController for cancellations and timeouts.
  • Keep headers explicit for content type.
  • For large responses, prefer streaming over buffering the entire payload in memory.