JavaScript Fetch — Are You Handling Responses Correctly?
What many developers are not aware of.
Every JavaScript developer probably knows the fetch API. It provides an easy interface for making requests to endpoints, for fetching or posting data. However, many developers are not aware of a common mistake when using fetch.
Part of what made fetch so popular is that it returns a promise. The advantage of a promise is that it can be handled elegantly with the handler functions: then
, catch
, andfinally
.
When the promise gets resolved, the then
function will execute and if the promise gets rejected, the catch
function will. Naturally, one would assume, that a successful response is handled in the then
function, and an unsuccessful one, in the catch
function. Surprisingly, this is not the case.
Trick question: When will a promise get resolved?
Is it whenever the HTTP response status code is 200–299?
Nope. Okay, different question. When will a promise get rejected?
Is it whenever the HTTP response status code is 400–499 or 500–599 (client or server error)?
Nope.
Let´s get to the bottom of this
If the server rejects your request with a 400–499 error, the promise is not rejected but resolved. For instance the common 401 — not authorized response code, would lead to a resolved promise.
Even if the server you are trying to fetch data from is currently facing problems and a 500 internal server error is returned, the promise will still resolve and the then
handler will therefore be called. Not the catch
handler.
As long as the server returns a response, ANY response, the promise will resolve. The promise will only be rejected if the server was not able to send a response to your request at all. This could happen for instance, due to a network error or if the server caught fire (dramatic I know).
What does that mean for developers using fetch
This means that you cannot only handle successful responses (response code == 2xx) in the then
handler. You also have to handle unsuccessful responses there (response code != 2xx).
The easiest way to identify whether or not a response was successful, is with the Response object's ok
attribute.
The Response object contains the attribute ok
, which is a boolean. Response.ok
is true, if the response status code are in the successful (2xx) range. This is why some developers check if the status code is between 200 and 299, but checking Response.ok will actually do the exact same just shorter.
If response.ok
is true, we proceed with some logic, but if it was false, the response was not successful and we need to deal with the error. A common way to do that, is to throw an error, which will cause the catch
handler to be called. This way the treatment of the successful and the unsuccessful response can be separated by handler functions and the catch
handler can deal with non-successful responses and rejected promises all together.
fetch("https://jsonplaceholder.typicode.com/todos")
.then(response => {
if (response.ok) {
console.log(response); // do something with successful response
}
else {
throw new Error(`HTTP error, status = ${response.status}`);
}
})
.catch(error => {
console.error(error);
})
Under the hood, this works because the then
method itself also returns a promise. Throwing an error leads to that promise getting rejected, and for catch
to get called. For more details on promises and error handling check out this helpful resource.
The takeaway
The important takeaway is to always check for response.ok
in the then
handler before executing the code that assumes a successful response. It can also be helpful to throw an error if you want to deal with an unsuccessful response further in the catch statement.
Thanks for reading! How do you handle unsuccessful responses from the fetch api? Let me know in the comments. If you learned something new, follow me on Medium for more web dev content.
Boost your frontend skills by creating an interactive resume builder with my beginner-friendly, hands-on Vue.js course.
More content at PlainEnglish.io. Sign up for our free weekly newsletter. Follow us on Twitter, LinkedIn, YouTube, and Discord.