DEV Community

Cover image for Fetch API, do you really know how to handle errors?
Diona Rodrigues
Diona Rodrigues

Posted on • Edited on • Originally published at dionarodrigues.dev

Fetch API, do you really know how to handle errors?

The Fetch API is not that new and most developers have used it at some point to retrieve resources from a server, but do you really know how to handle errors when using this API?

Given the code below, in which a request using the Fetch API returns a 404 error, what would be the message in the console: successful or failed?

try {
  // this endpoint will return 404
  const response = await fetch('https://restcountries.com/v4.1/all');
  console.log('Success');
} catch {
  console.error('Failed');
}
Enter fullscreen mode Exit fullscreen mode

If you assume this code logs the failed message when a 404 error occurs, I’d say this article is definitely for you because your answer is wrong. The console.log will display the successful message and I’ll tell you why and what’s the best way to handle errors using the Fetch API.

What’s Fetch API?

In short, the Fetch API is an interface for easily making asynchronous HTTP GET and POST requests to endpoints.

I really recommend you read this article I wrote a while ago called “Fetch API is new old version of AJAX”, where I explained better what Fetch API is and how to use it along with the async/await syntax, so feel free to check out for more explanation about this API.

How to handle errors using Fetch API?

When using the Fetch API different errors may occur, such as: server error (500), not found error (404), network error, CORS error and so on. And we have different approaches to handle all these errors, as you can see below.

Using try/catch to handle Fetch API errors

I'm assuming you're familiar with promises in JavaScript and know how they work (because one of the reasons the Fetch API is so popular is because it returns a promise). Therefore, we can use the power of then, catch and finally when the fetch is resolved, rejected and finished.

Wrapping the fetch call in a try/catch block is a very common way of handling errors (see example below), but not all errors can be caught and I will explain this just after the code.

try {
  // this endpoint will return CORS error
  const response = await fetch('https://google.com/api');
} catch {
  console.error('Failed');
}
// Output: Failed
Enter fullscreen mode Exit fullscreen mode

This code will attempt to perform a fetch and detect errors only when the promise is rejected (which can occur in two scenarios):

  • network errors: failure to connect to the server which can be caused by several reasons, such as slow network and timeout, for example.
  • CORS errors: when a domain is not authorised to obtain resources from a different domain.

Fetch will return resolved for server status errors (like 404 and 500 for instance), that's the reason why catch can't get them in the example at the beginning of this article.

Checking the response status to handle Fetch API errors

Another common way to handle errors using the Fetch API is to check the response status when the promise resolves - and this is how we should get the 404 error in the example at the beginning of this article:

// this endpoint will return 404
const response = await fetch('https://restcountries.com/v4.1/all');

if (response.ok) {
  // ...do something with the response if response.ok is true
} else {
  console.error('Failed');
}
Enter fullscreen mode Exit fullscreen mode

Here we use response.ok to check if the response was successful or not (true or false).

  • it is true when the status code is between 200 and 299 (it means successful).
  • it is false when the server returns any other status than the above. Examples are 404 (not found) and 500 (internal server error).

Best way to handle errors using Fetch API

Example 01

As you saw above, try/catch and response.ok are used to catch different types of errors, so we can combine the two approaches to better handle errors using the Fetch API. See example below:

try {
  const response = await fetch('https://restcountries.com/v4.1/all');
  if (response.ok) {
    console.log('Promise resolved and HTTP status is successful');
    // ...do something with the response
  } else {
    console.error('Promise resolved but HTTP status failed');
  }
} catch {
  console.error('Promise rejected');
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • try/catch is used to get errors when the promise gets rejected (network or CORS issues)
  • response.ok is used to handle server errors (404 or 500, for example) when the promise gets resolved

Example 02

Another common way to handle erros inside the try block is throwing an error when response.ok isn’t true in order to make the catch block be executed, so that we can handle all errors at the same place. See example bellow for a better understanding:

try {
  const response = await fetch('https://restcountries.com/v4.1/all');

  if (response.ok) {
    console.log('Promise resolved and HTTP status is successful');
    // ...do something with the response
  } else {
    // Custom message for failed HTTP codes
    if (response.status === 404) throw new Error('404, Not found');
    if (response.status === 500) throw new Error('500, internal server error');
    // For any other server error
    throw new Error(response.status);
  }
} catch (error) {
  console.error('Fetch', error);
  // Output e.g.: "Fetch Error: 404, Not found"
}
Enter fullscreen mode Exit fullscreen mode

Here we throw errors to handle them in the catch block and also display a custom message in the console depending on the type of error.

  • when response.ok returns false, we can simple throw an error, so the catch block will be executed
  • catch will then handle all types of errors
  • catch accepts an argument that can be customized when throwing errors from the try block
  • response.status can be used to check the returned HTTP status code to display customized messages in the console (like 400, 404, 500…).

References

Conclusion

It is very important to handle all errors that may occur when using Fetch API when retrieving resources from the server and here I have explained how to use try/catch along with response.ok and response.status to get different errors and also customize the way we are dealing with them.

See you next time! 😁

Top comments (43)

Collapse
 
georgehvm profile image
George Henrique

Amazing post!

I always tend to use fetch instead axios because it's native and with the knowledge acquired now it'll only just get better :)

Collapse
 
hilleer profile image
Daniel Hillmann • Edited

I usually question fellow colleagues why we should use fetch, native or not, over other (well-tested) libraries like axios where these examples provided in this post are provided out-of-the-box, rather than re-defining the same fetch util checks ourselves.

Collapse
 
appqui profile image
Igor Golodnitsky • Edited

(well-tested) libraries like axios

Always curious why send to browser 25 kb minified library, which does practically nothing. While we can use native code, which is doing the same, and cost nothing.

Collapse
 
dionarodrigues profile image
Diona Rodrigues • Edited

Thanks George!
Fetch API is very powerful and now it's also available in Node.js.

Collapse
 
cmgustin profile image
Chris Gustin

Great post! Simple and informative. I feel like I’ve had both of these methods (try/catch and response checking) explained in various courses and tutorials but this is the first time I’ve seen it all put together and explained clearly (or maybe it’s just the first time it’s clicked for my brain).

Collapse
 
dionarodrigues profile image
Diona Rodrigues

Happy to hear that @cmgustin !! :)

Collapse
 
ahangarha profile image
Mostafa Ahangarha

Why are you checking if (!response.ok) throw... again in the else block?

Collapse
 
dionarodrigues profile image
Diona Rodrigues • Edited

Hey, If you’re talking about the last code snippet (example 2), this is the explanation: after checking if the error is 404 or 500, we need a way to check any other kind of failed response, which can be done by using ‘!response.ok’. So, if the server returns 400, for example, this code will throw an error as well. For 404 and 500, we display customized messages, that’s the reason we use ‘response.status’ for them.

Please, let me know if I answered your question, otherwise I’d like you provide me with more detail about which example you are talking about.

Collapse
 
tudev profile image
Takashi Uemoto • Edited
if (response.ok) {...}
else { 
  if-
  // if(!response.ok) throw new Error(response.status) // redundant if-clause.
  throw new Error(response.status)
}
Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
dionarodrigues profile image
Diona Rodrigues • Edited

I see what you both mean!
Code snippet updated. Thanks a lot for the review.

Collapse
 
michaeltharrington profile image
Michael Tharrington

This is an awesome post, Diona! Really nicely done. 🙌

Collapse
 
dionarodrigues profile image
Diona Rodrigues

Many thanks Michael!!! 🫶

Collapse
 
stwissel profile image
Stephan H. Wissel

It gets really interesting when you get the more unusual response codes like 3xx or 1xx. Should you trust the automated redirect or look at the location header yourself?
In case of 1xx -> should you poll for results? The fetch API supports all this, so the ball is firmly in your application logic’s court

Collapse
 
joxx profile image
joachim kliemann

Great article, thanks for posting

Collapse
 
dionarodrigues profile image
Diona Rodrigues

Thanks!!!!!

Collapse
 
kristjanesperanto profile image
Kristjan ESPERANTO

An ESLint rule for that would be nice 😃

Collapse
 
dionarodrigues profile image
Diona Rodrigues

That’s a nice idea!!!

Collapse
 
zhangzhengqiqi profile image
Aolef

Great post! The post make me learn more about fetch!

Collapse
 
iwnow profile image
Vostroknutov Vitaliy • Edited

Nice! Need article about catch with AbortController :)

Collapse
 
console_x profile image
F. Güngör

thanks for posting

Collapse
 
dionarodrigues profile image
Diona Rodrigues

You’re welcome! I hope you enjoyed it!! :)

Some comments have been hidden by the post's author - find out more