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 ?
-
400? A proper fix for this might be to re-architect an entirely new way to get the data. Maybe make 1 request with parameters and having the API patchwork all the data together. – Corey Ogburn Oct 25 '16 at 19:38
-
@CoreyOgburn I believe the OP means a `4xx` HTTP error code. – Thorn G Oct 25 '16 at 19:43
-
@TomG That makes a lot more sense. – Corey Ogburn Oct 25 '16 at 19:44
-
Yes, I mean 400 error code, for example invalid password when someone login – Artur Kasperek Oct 25 '16 at 19:57
-
Actually, @ArturKasperek, I think that an invalid password would properly throw a 409 Conflict status: the server was able to process that request but found that there were issues related to the resource (i.e., the user). – JESii Dec 23 '18 at 12:38
4 Answers
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
})
}
-
1So we're forced to use `then` even if the rest of the code uses `async`/`await`? – sleighty May 19 '22 at 01:38
-
@sleighty - No, you can use `await` just fine with `fetch()` if you want. – jfriend00 May 19 '22 at 01:45
-
1I actually realized that [there's just no way to get rid of the 404](https://stackoverflow.com/a/7041565/3403247) being printed to the console, even if you `.catch()` or `try`/`catch` it. – sleighty May 25 '22 at 04:04
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 });
}
})

- 10,797
- 21
- 82
- 134
Incorporating it into your HTTP abstraction is probably a good idea. Perhaps with some sort of options
argument:
const myFetch = (method, path, {headers, strictErrors, whatever}) => {
// fetch here, if strictErrors is true, reject on error.
// return a Promise.
}
myFetch('GET', 'somepath', {strictErrors: true})
.then(response => {})
.catch(err => { /* maybe 400 */ });
A wrapper around fetch
is generally a good idea, fetch
is a relatively low level function. Just as it isn't a good idea to directly create new XHR objects everywhere, I believe it isn't a good idea to directly call fetch()
in various parts of your application. It's akin to a global variable, in some ways.

- 172,118
- 50
- 264
- 308
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)

- 1,287
- 12
- 26