11

A fetch API request will only fail if there is a network or server error. So for example, if I execute the following code, assuming it went through the try block without an error, I will have a valid populated res.

try {
    const res = await fetch('/createurl', { 
        method: 'POST',
        body: 'testData',
        headers: {
            'Content-Type': 'application/json'
        }
    })

    if (res.ok) {
        alert('Resource created!')
    } else {
        alert('Error creating resource!')
    }

    flashResponseToUser(res)
} catch(e) {
    alert('A server or network error occurred during the request!')
}

I am handling res to show the users the necessary error or success message using the flashResponseToUser(res) function. Since res.json() returns a Promise, flashResponseToUser has to be an async function.

const flashResponseToUser = async(res) => {
    const jsonRes = await res.json() // Get data from response
    console.log(jsonRes)
}

I want to know:

  1. Why does res.json() return a Promise since at this point the response has already been received by the client?
  2. Under what conditions would the Promise returned by res.json() fail?
  3. Does the code within flashResponseToUser(res) also need to be wrapped within a try-catch block since I am using res.json()?
philosopher
  • 1,079
  • 2
  • 16
  • 29
  • https://developer.mozilla.org/en-US/docs/Web/API/Body this will be able help you with those questions. – sshanzel Jan 14 '20 at 07:16
  • 1
    @Siege21x I did read that but it still doesn't explain why res.json() returns a Promise instead of returned data directly. Also doesn't tell us under what conditions res.json() fails. – philosopher Jan 14 '20 at 07:19
  • [answer](https://stackoverflow.com/questions/37555031/why-does-json-return-a-promise) for first question. [answer](https://stackoverflow.com/questions/37280029/whatwg-fetch-fails-when-json-parsing-an-empty-response-how-can-i-prevent-it) for second question. – SuleymanSah Jan 14 '20 at 07:31

1 Answers1

18

Why does res.json() return a Promise since at this point the response has already been received by the client?

fetch returns a Response object. This indicates that the headers of the response have been received, but does not necessarily mean that the whole response has been received - imagine, for example, when you load a huge page. It's not exactly the same thing, but you'll receive the headers and the browser will start to load the response even though there's still more to download. The Response object provides the headers and a way to handle still-incoming data.

Under what conditions would the Promise returned by res.json() fail?

It might fail if the response wasn't in proper JSON format. For example, if the plain text of the response was Internal Server Error, which isn't JSON. Here's an example:

(async () => {
  const response = await fetch('data:,Internal%20Server%20Error');
  console.log('got response');
  try {
    await response.json();
  } catch(e) {
    console.log('error:', e.message);
  }
})();

Does the code within flashResponseToUser(res) also need to be wrapped within a try-catch block since I am using res.json()?

If you wanted to be perfectly safe, yes. But, in most situations, it's easiest just to catch in one place, where you can handle the error. Instead of handling possible errors at every step of the process, you might handle the error just once, in the consumer, eg:

const getInfo = async () => {
  const res = await fetch('/createurl', { 
    method: 'POST',
    body: 'testData',
    headers: {
      'Content-Type': 'application/json'
    }
  })

  if (!res.ok) {
    throw new Error(res.status);
  }
  return res.json();
};
getInfo()
  .then(flashResponseToUser)
  .catch(() => {
    alert('A server or network error occurred during the request!')
  })

(assuming that flashResponseToUser will never throw, if provided with an expected object. If flashResponseToUser might throw anyway, you can separate out the .catches to distinguish network errors from other runtime errors)

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • Thanks! Great explanation. Will mark this as the answer as soon as I can! – philosopher Jan 14 '20 at 07:23
  • 1
    JSON format failure happens more often than one would think. Sometimes your framework will return an error page that is HTML instead of json. Sometimes your reverse proxy (nginx or apache front-end) will return an error page that is HTML instead of json. Sometimes your office or ISP caching server will return an error page that is HTML instead of json. You don't necessarily have control of all conditions – slebetman Jan 14 '20 at 12:04
  • 1
    Oh, don't forget that public Wifi will sometimes hijack your request and return a login page that is HTML instead of json – slebetman Jan 14 '20 at 12:06