The KNVB
The KNVB

Reputation: 3844

How to catch HTTP response error and then pass to the caller

I have an API to update data to the database.

I am using Fetch API to send data to the server API for storing data in the database.

In case the webserver cannot update data to the database, it returns a 500 error code and an error message.

And the response JSON as the following:

{"status":"error","message":"Some wrong when update roster data."}

My problem is that I cannot forward the message to the fetch API caller.

It is my fetch API source code:

static async fetchAPI(url,method,getParams,postParams){
  if (getParams){
      const paramsObject = new URLSearchParams(getParams);
      const queryString = paramsObject.toString();  
      url+="?"+queryString;
  }
  url="/rosterWeb"+url;
  console.log("=======================");
  console.log("url="+url);
  console.log("method="+method);
  console.log("getParams="+getParams);
  console.log("postParams="+postParams);
  console.log("=======================");
  return fetch(url,
                {
                    body: JSON.stringify(postParams),
                    headers:{
                        'Content-Type': 'application/json' 
                    },
                    "method":method || 'GET',
                })
          .then(response =>{
            if (response.ok){
              return response.json();                
            } else {
              if (response.status===500){
                response.json()
                .then(json=>{
                  throw new Error(json.message);
                })
              }
            }  
          })
          .catch(error =>{
            console.error('Error:', error);
          });              
                

}

This is my middleware code snippet:

import Utility from './Utility';
export default class Roster{
  ..............
  .......
  constructor(){
    this.saveToDB=async(data)=>{
        return await Utility.fetchAPI(*saveDataURL*,POST',null,rosterData);
    }
  }
}

This is my UI component code snippet:

export default function ButtonPanel(){
 ..............................
 
 async function saveDataToDB(){
    
    let roster=new Roster();

    await roster.saveRosterToDB({
       ........................
       ...........................
    })
    .then(result=>{
        console.log("Update Success")
    })
    .catch(error=>{
        console.log("ButtonPanel Exception Caught");
        console.log(error);
    })
    /*
    try{
        let result=await roster.saveRosterToDB({
            month:rosterMonth.getMonth()+1,
            preferredShiftList:rosterData.preferredShiftList,
            rosterList:rosterData.rosterList,
            year:rosterMonth.getFullYear(),
        })
        console.log(result);
    }catch(error){
        console.log("Exception caught.");
        console.log(error.message);
    };
    */
    roster=null;
  }
}

When I execute the Roster.saveRosterToDB method, both try-catch and then-catch structure, it also returns the following error:

`Unhandled Rejection (Error): Some wrong when update roster data.
(anonymous function)
c:/Users/knvb/workspace/rosterWeb_react_node/src/utils/Utility.js:91`

Where "Some wrong when update roster data" is come from response JSON.

And the ButtonPanel.saveDataToDB print the statement "Update success" to the console.

I tried to forward the message from the server to ButtonPanel via an exception.

However, the exception/error does not be triggered, how can I fix it?

Upvotes: 2

Views: 5147

Answers (2)

T.J. Crowder
T.J. Crowder

Reputation: 1074028

The main problem is that the place you're doing throw new Error(json.message) just causes a rejection of the promise from the fulfillment handler it's in, but that promise isn't connected to the chain you have a catch on. It would be if you added return. E.g., change:

response.json()
    .then(json => {
        throw new Error(json.message);
    })

to

return response.json()
    .then(json => {
        throw new Error(json.message);
    })

But there are a few other issues:

  1. fetchAPI is an async function, so there's no reason to be using .then and .catch in it. Use await.

  2. What if response.ok is false but response.status isn't 500? Your code falls through and ends up fulfilling the promise from fetchAPI with undefined with no indication of why it hasn't returned anything. It should raise some kind of error in that case as well.

  3. fetchAPI hides all errors, disguising them as results with the value undefined, because of the catch handler at the end. That's an anti-pattern. Don't handle errors in fetchAPI, handle them where it's used, so that the code where it's used knows whether the operation succeeded or failed.

Here's how I'd suggest you update fetchAPI:

static async fetchAPI(url, method, getParams, postParams) {
    if (getParams) {
        const paramsObject = new URLSearchParams(getParams);
        const queryString = paramsObject.toString();
        url += "?" + queryString;
    }
    url = "/rosterWeb" + url;
    console.log("=======================");
    console.log("url=" + url);
    console.log("method=" + method);
    console.log("getParams=" + getParams);
    console.log("postParams=" + postParams);
    console.log("=======================");
    const response = await fetch(url, {
        body: JSON.stringify(postParams),
        headers: {
            'Content-Type': 'application/json'
        },
        "method": method || 'GET',
    });
    if (response.status === 500) {
        const error = await response.json();
        throw new Error(error.message);
    } else if (!response.ok) {
        throw new Error(`HTTP error ${response.status}`);
    }
    return response.json();
}

When using it, if its promise is fulfilled, you know it's the data from the API. If its promise is rejected, you know it isn't the data from the API and that you should handle/report an error.

Upvotes: 2

Vitalii
Vitalii

Reputation: 2131

Seems like return was missing in 500 status handling block

          fetch(url,
                {
                    body: JSON.stringify(postParams),
                    headers:{
                        'Content-Type': 'application/json' 
                    },
                    "method":method || 'GET',
                })
          .then(response =>{
            if (response.ok){
              return response.json();                
            } else {
              if (response.status===500){
                return response.json() // return was missing here
                .then(json=>{
                  throw new Error(json.message);
                })
              }
            }  
          })
          .catch(error =>{
            console.error('Error:', error);
          });     

The code above during 500 status will return a promise.

It'll first get the JSON payload from 500 response and will throw an error with the message from JSON, which will mean that returned promise will become rejected and all catch blocks attached to that promise (including the one with console.error()) will be called.

Upvotes: 2

Related Questions