beyondtdr
beyondtdr

Reputation: 451

How to handle async fetch undesired result?

I have the following React Native client code:

confirmMatchRecord(userId, matchedUserData['userId'], matchRecordData['matchRecord']['matchWinner'], matchRecordData['matchType'], currentUserRating, matchedUserRating, matchData['_id'], matchRecordData['matchRecord']['_id'], airbnbRatingValue, true, new Date())
.then((results) => {
    // Do stuff
})
.catch((error) => {
    Alert.alert('Error', 'There was an issue with confirming the record. Please check your connection and/or try again later.');
});

And the following code in my confirmMatchRecord function:

export async function confirmMatchRecord(userId, matchedUserId, matchWinner, matchType, currentUserRating, matchedUserRating, matchId, matchRecordId, matchRating, matchConfirmed, timestamp) {
  console.log('Attempting to record match');

  info = { userId, matchedUserId, matchWinner, matchType, currentUserRating, matchedUserRating, matchId, matchRecordId, matchRating, matchConfirmed, timestamp }

  const firebaseIdToken = await AsyncStorage.getItem('@firebaseIdToken')
  const requestOptions = {
      method: 'POST',
      headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + firebaseIdToken },
      body: JSON.stringify(info)
  };
  const response = await fetch(ngrokOrLocalhost + '/confirmmatchrecord', requestOptions)
    if (response['Status']==='Failure') {
        // throw new Error(`HTTP error! status: ${response.status}`);
        throw new Error(400);
    } else if (response['Status']==='Success') {
        const data = await response.json()
        return data
    }
}

Server code:

router.post('/confirmmatchrecord', async (req, res) => {
// Do a lot of stuff

if (response==='Success') {
    return res.status(200).json({'Status': 'Success'})
} else {
    return res.status(400).json({'Status': 'Failure'})
    console.log('Match record was not confirmed successfully');
}

When response['Status']==='Failure (sent by server) it throws an error 400 as you can see, I was hoping to trigger the .catch in the client code then. But that does not happen, because the client code continues to run on the .then part.

How should I do this instead? Not sure if using .catch here is even correct or if I should do this type of work another way.

Upvotes: 0

Views: 57

Answers (1)

T.J. Crowder
T.J. Crowder

Reputation: 1075239

You seem to be aware of the bit of a footgun in the fetch API (I write about it here) where fetch only rejects its promise on network errors, not HTTP errors, but your check is incorrect in a couple of ways:

  • It's status, not Status (capitalization matters), and

  • It's the HTTP code (400 for instance), not a string

The Response object provides a convenient ok flag that's true for any successful response and false otherwise, so:

const response = await fetch(ngrokOrLocalhost + '/confirmmatchrecord', requestOptions)
if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`); // This will be "HTTP error! status: 400" if the HTTP error code is 400
}
const data = await response.json();
return data;

In a comment you've said:

My response['Status'] was checking for a custom server message I had sent (res.status(400).json({'Status': 'Failure'}), I updated the post with it. Not sure why it didn't catch that

Ah! Okay. The reason it didn't catch it is that you're looking for it on the Response object, but your JSON is in the response body.

I suspect you don't want to use your own Status anymore since you know about response.ok and response.status now, but if you ever do want to include your own information in an error response as JSON, you can do that. You'd do it like this:

const response = await fetch(ngrokOrLocalhost + '/confirmmatchrecord', requestOptions)
const data = await response.json(); // Expects JSON in *both* the success response and the error response
if (data.Status === "Failure") {
    throw new Error(`HTTP error! status: ${response.status}`); // This will be "HTTP error! status: 400" if the HTTP error code is 400
}
return data;

But I'd stick with just the built-in ok and status for pure success/failure information. This could be handy if you wanted to provide more details of the failure, though.

Upvotes: 2

Related Questions