Search code examples
reactjsfetchresponsehttp-status-code-400

How do I capture the response JSON from a 400 bad request with fetch?


I'm using React 17. I want to submit a fetch request and parse the error JSON that comes back in the event of a 400. I have this code

fetch(REACT_APP_PROXY + "/save_to_sheet_from_form/", {
  method: "post",
  headers: {
    "Content-Type": "application/json",
  },
  body: formData,
})
  .then((response) => {
    if (response.ok) {
      return response.json();
    } else {
      throw response;
    }
  })
  .then(function (data) {
    window.location.reload();
  })
  .catch((err) => {
    err.json().then((errorMessage) => {
      try {
        setErrors(JSON.parse(errorMessage));
      } catch (e) {
        return;
      }
    });
  });

In my network tab, I can see this response when there is a 400

{"coop_name":["This field may not be blank."],"street":["This field may not be blank."],"city":["This field may not be blank."],"zip":["This field may not be blank."],"contact_name":["This field may not be blank."],"contact_email":["This field may not be blank."],"contact_phone":["This field may not be blank."]}

However the above line

err.json().then((errorMessage) => {

is throwing a syntax error. What's the proper way to retrieve the body of the response?


Solution

  • If the response, when not OK, may still be JSON-parseable, then using .json on it will have the browser attempt to parse the result as JSON.

    You need to account for a few separate categories:

    • The initial request failed (in which case the first .then will not be entered into at all, and the .catch will be entered into instead)
    • The request was bad, and the server responded with parseable JSON
    • The server had a problem and sent a bad response back (such as 500 Internal Server Error)
    • The request succeeded

    You can put most of the logic testing for this while parsing the header of the response. Something along the lines of

    fetch(REACT_APP_PROXY + "/save_to_sheet_from_form/", {
        method: "post",
        headers: {
            "Content-Type": "application/json",
        },
        body: formData,
    })
        .then((response) => {
            if (response.ok) {
                // will succeed unless server logic or your logic is off
                return response.json().then((data) => {
                    window.location.reload();
                });
            } else if (response.status === 400) {
                // will succeed if the server will always respond with JSON with a 400 response
                return response.json().then((errorObj) => setErrors(errorObj));
            } else {
                // there was some other error in the response, such as status 500
                setErrors(response.statusText);
            }
        })
        .catch((err) => {
            // An unexpected error occurred which was not 400 nor while parsing the response header
            setErrors(String(err));
        });
    

    If the logic to carry out when the request succeeded or failed isn't entirely trivial, go ahead and use standalone named functions instead to make it more readable instead of putting everything in the first .then. (eg if (response.ok) { return response.json().then(handleSuccess); })