How to handle HTTP code 4xx responses in fetch api
Solution 1
I'd suggest a wrapper that checks response.ok
which will be true if the response code is 2xx.
Note this statement from the MDN page on fetch()
:
An accurate check for a successful fetch() would include checking that the promise resolved, then checking that the Response.ok property has a value of true. An HTTP status of 404 does not constitute a network error.
Here is a wrapper like this:
function fetchData() {
return fetch.apply(null, arguments).then(response => {
if (!response.ok) {
// create error object and reject if not a 2xx response code
let err = new Error("HTTP status code: " + response.status)
err.response = response
err.status = response.status
throw err
}
return response
})
}
Solution 2
This way we can handle all types of status accordingly.
fetch(url, {
method: 'POST',
headers: headers,
body: JSON.stringify({ user_email: email }),
}).then((response) => {
return new Promise((resolve) => response.json()
.then((json) => resolve({
status: response.status,
ok: response.ok,
json,
})));
}).then(({ status, json, ok }) => {
const message = json.message;
let color = 'black';
switch (status) {
case 400:
color = 'red';
break;
case 201:
case 200:
color = 'grey';
break;
case 500:
default:
handleUnexpected({ status, json, ok });
}
})
Solution 3
The best approach I've found for this is to wrap it in a new Promise, and if response.ok
is false, reject the Promise with the error context.
/**
* Parses the JSON returned by a network request
*
* @param {object} response A response from a network request
*
* @return {object} The parsed JSON, status from the response
*/
function parseJSON(response) {
return new Promise((resolve) => response.json()
.then((json) => resolve({
status: response.status,
ok: response.ok,
json,
})));
}
/**
* Requests a URL, returning a promise
*
* @param {string} url The URL we want to request
* @param {object} [options] The options we want to pass to "fetch"
*
* @return {Promise} The request promise
*/
export default function request(url, options) {
return new Promise((resolve, reject) => {
fetch(endpoint + url, options)
.then(parseJSON)
.then((response) => {
if (response.ok) {
return resolve(response.json);
}
// extract the error from the server's json
return reject(response.json.meta.error);
})
.catch((error) => reject({
networkError: error.message,
}));
});
}
(Top comment on https://github.com/github/fetch/issues/203)
Artur Kasperek
Updated on June 20, 2022Comments
-
Artur Kasperek almost 2 years
I was wondering how we should handle 400 from backend when we use ajax function. We can make if statement in promise resolve function and check if res status is 400. Different approach is making wrapper service for fetch, and when we got 400 from server we throw exception. How to deal with that problem ?
-
sleighty almost 2 yearsSo we're forced to use
then
even if the rest of the code usesasync
/await
? -
jfriend00 almost 2 years@sleighty - No, you can use
await
just fine withfetch()
if you want. -
sleighty almost 2 yearsI actually realized that there's just no way to get rid of the 404 being printed to the console, even if you
.catch()
ortry
/catch
it.