JavaScript Fetch API Error Handling

Web Development

The JavaScript Fetch API is used to make HTTP requests and handle responses in the browser. It is a replacement for the outdated XMLHttpRequest object and has more options for customizing the request, like adding headers or query parameters. It’s based on Promises therefore allows cleaner code for handling asynchronous operations.

A simple request that expects JSON as content type can look like this:

async function load() {
  const response = await fetch('https://api.github.com/repos/getify/You-Dont-Know-JS/issues?per_page=5');
  const data = await response.json();
  console.log(data);
}

load();

Pretty simple. But there are a lot of errors that can happen during the request:

  • The requested resource can be offline or not available
  • The request might be successful, but the server might return an HTTP error
  • The request might be successful, the server might return HTTP status 200 OK, but parsing the response body can fail (e.g. because JSON is expected but got something else like HTML or XML)
  • The request may be canceled before it completes, e.g. it may be possible to cancel an request before starting a new one

The following code takes care of these cases:

let isLoading = false;
let hasError = false;

async function load() {
  try {
    isLoading = true;

    const response = await fetch('https://api.github.com/repos/getify/You-Dont-Know-JS/issues?per_page=5');

    if (!response.ok) throw new Error('Response was not OK.');

    const data = await response.json();
    console.log(data);

    hasError = false;
  } catch (e) {
    hasError = true;
    console.error('Error loading GitHub issues: ', e.message);
  } finally {
    isLoading = false;
  }
}

load();

The code performs a request to GitHub to load issues from a repository. If there is a network problem, the response is not OK or the response body can not be parsed the error is caught and a message is printed to the console.

There are also two boolean variables isLoading and hasError to set a state of the request.

It’s possible to react to different types of errors or throw other types of errors. As an example there could be an NotFoundError if the status code is 404.

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

setTimeout(() => ac.abort(), 300); // Simulate abort

let isLoading = false;
let hasError = false;

async function load() {
  try {
    isLoading = true;

    const response = await fetch('https://api.github.com/repos-will-fail/getify/You-Dont-Know-JS/issues?per_page=5');

    if (!response.ok) {
      if (response.status === 404) {
        throw new NotFoundError('Not Found');
      }
      
      throw new Error('Response was not OK.');
    }

    const data = await response.text();
    console.log(data);

    hasError = false;
  } catch (e) {
    if (e instanceof NotFoundError) {
      console.error('Resource not found.');
      return;
    } else if (e.name === AbortError) {
      console.error('Request was aborted');
    }
  
    console.error('Error loading GitHub issues: ', e.message);

    hasError = true;
  } finally {
    isLoading = false;
  }
}

class NotFoundError extends Error {
  constructor(message) {
    super(message);
    this.name = 'NotFoundError';
  }
}

load();

The code above implements a custom error NotFoundError that extends Error. If the response of the request has a status code of 404 the custom error is thrown. Inside the catch block there is a check for the custom error, otherwise the general console error message is printed.

As mentioned above the fourth case can be that a request is aborted. To abort a request a AbortController is needed. The created signal must then be passed as an option of the fetch operation.

If the request is aborted an AbortError will be thrown. The type of the error is DOMException and the name is AbortError. This is why the name is used to check for an AbortError.

The Fetch API is great for making HTTP requests. It’s important to understand how to handle errors to ensure that requests are reliable and help provide a good user experience.

Links