Return Errors With Async/await Fetch

02/03/20191 Min Read — In JavaScript

I was wondering how one could display the error of a fetch Promise in React.

Here is a an async fetch API call with a try/catch block. The function takes an input string, makes a POST request and returns a Promise if successful. It parses the response with json(). Otherwise it throws an error.
In the catch block I return the error object.

You have to both throw an Error if the response.ok is false and catch an error. Why?

from MDN:

The Promise returned from fetch() won’t reject on HTTP error status even if the response is an HTTP 404 or 500. Instead, it will resolve normally (with ok status set to false), and it will only reject on network failure or if anything prevented the request from completing.

const apiKey = process.env.REACT_APP_API_KEY

export const apiCall = async input => {
  try {
    const config = {
      method: 'POST',
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json',
        apiKey: apiKey,
      },
      body: JSON.stringify({
        destination: input,
      }),
    }
    const url = 'https://someurl.com'
    const response = await fetch(url, config)
    if (response.ok) {
      const res = await response.json()
      return res
    } else {
      throw new Error('Network response was not ok.')
    }
  } catch (error) {
    return error
  }
}

In many examples, you'll see a simple console.log() of the error. But of course, you can also return it.

In the example above, the function outputs either a [Response object](https://developer.mozilla.org/en-US/docs/Web/API/Response) parsed to JSON or an Error object.

I have a main React app component. It has an InputForm component with an event handler that fires off the api call.
Both the api Error and the api (successfull) response will be handled as state.

// all the imports

class App extends React.Component {
  state = { res: null, error: null };

  onSubmit = async ({ input }) => {
    const response = await apiCall(inputUrl);
    if (response instanceof Error) {
      this.setState(() => ({ error: response }));
    } else {
      this.setState(() => ({ res: response }));
    }
  };
  render() {
    const { res, error } = this.state;
    return (
      <div">
        // some components
        <Inputform onSubmit={this.onSubmit} />
        {res && <Result res={res} />}
        {error && <ErrorMessage error={error} />}
      </div>
    );
  }
}

The trick is to check for the type of return object (is it an Error object?) and then handle it accordingly with setState().
Then the App component will re-render and forward the state to the two subcomponents Result or ErrorMessage. These subcomponents will then display the results.

Further Reading