2 ways of handling GraphQL errors in Apollo Client (React)

Marcin Kwiatkowski /
2 ways of handling GraphQL errors in Apollo Client (React)

If you use Graphql Apollo Client with React, there are two ways (more precisely speaking) – two levels of handling errors:

  • - operation level

  • - application level

Operation level errors handling

In this case, you have access to the dataloading, and error fields, and you can use an error object, which can be used to show a conditional error message.

1const { loading, error, data } = useQuery(YOUR_QUERY);
2
3if (error) return <p>Error :(</p>;

Of course, you can create a component responsible for displaying errors in your app.

1import React from 'react';
2import PropTypes from 'prop-types';
3
4import classes from './ErrorMessage.module.css';
5
6
7const ErrorMessage = (props) => {
8    const { error, ...rest } = props;
9
10    const shouldDisplayError = error && error.message ?
11        <div className={classes.errorMessage} {...rest}>{error.message}</div>
12        :
13        null
14    ;
15
16    return shouldDisplayError;
17};
18
19ErrorMessage.propTypes = {
20    error: PropTypes.shape({
21        message: PropTypes.string.isRequired
22    })
23};
24
25export default ErrorMessage; 

This component is really straightforward. It receives an error as a prop and displays that error to the user.

Application-level error handling

Another approach is handling errors in Application-level. It allows you to create more complex logic.

Application-error handling lets you do whatever you want with errors. For example, you can log those errors to the console in development mode or use external tracking error tools like Sentry on production.

You can use this mechanism to display messages to the user as well. Let’s imagine that you have Messages Context in your app or you have a custom hook, and there you keep the whole logic for adding/removing/displaying messages.

If you use application-level error handling, you can pass error messages to your messages/notification manager and do whatever you want with them.

There are two types of errors.

  1. 1. GraphQL errors (like in a previous example)

  2. 2. Network error (for example, if the app lost internet connection)

GraphQL errors

There are three types of GraphQL errors:

  • - syntax error - for example, when you made a mistake in a query or mutation

  • - resolver error - for example, when the GraphQL server was not able to resolve a query field

  • - validation error - for example, when provided data didn't pass validation on the server side.

Note that when there is a resolver error, the GraphQL server returns partial data, but if there is a syntax or validation error, the server doesn't return data at all.

In the first case, the server responds with a 200 status code, otherwise returns a 4xx status code (for syntax and validation errors)

Network errors

Network errors occur when there are communication problems with the GraphQL server. In this case, the server usually responds with a 4xx or 5xx response status code and no data.

Error policies

By default, the Apollo server returns partial data when there is a resolver error, but you can change this behavior by changing the error policy. There are three error policies:

  • - none - the default one - if there are errors the graphQLErrorsthe field is populated and the data field is set to undefined (even if the server returns some data in response)

  • - all - both fields data and graphQLErrors are populated

  • - ignore - graphQLErrorsfield is ignored and not populated

How to specify error policy

You can specify error policy globally on or query/mutation level.

Global error policy

You can set an error policy for queries and mutations using the defaultOptions object in the ApolloClient constructor. The example below shows an error policy all set for queries and an ignore policy for mutations.

1import { ApolloClient, InMemoryCache } from '@apollo/client';
2
3const client = new ApolloClient({
4  cache: new InMemoryCache(),
5  uri: 'http://localhost:3000/',
6  defaultOptions: {
7    query: {
8      errorPolicy: 'all',
9    },
10    mutate: {
11      errorPolicy: 'ignore',
12    },
13  },
14});
15

Operation error policy

To specify error policy on the operation level, you have to pass the errorPolicy field in options object like this:

1const { loading, error, data } = useQuery(YOUR_QUERY, { errorPolicy: "ignore" });

Implement application-level error handling

To implement application-level error handling, we need to use a functionality called ApolloLink.

The Apollo Link library helps you customize the data flow between Apollo Client and your GraphQL server. You can define your client's network behavior as a chain of link objects that execute in a sequence.

Each link should represent either a self-contained modification to a GraphQL operation or a side effect (such as logging).

Take a look at a sample implementation of application-level error handling.

First, import the onError function.

1import { onError } from "@apollo/client/link/error";

Second, create the errorLink:

1const errorLink = onError(({ graphQLErrors, networkError }) => {
2    if (graphQLErrors) {
3        console.log(graphQLErrors);
4    }
5
6    if (networkError) {
7        // handle network error
8        console.log(networkError);
9    }
10});

Third, use HttpLink, and from helper method to combine a single link that can be used in the Apollo client.

1import { ApolloClient, InMemoryCache, ApolloProvider, from, HttpLink } from '@apollo/client';
2
3...
4
5const httpLink = new HttpLink({ uri: 'https://<API_URL>' })
6
7const appLink = from([
8    errorLink, httpLink
9])
10
11const client = new ApolloClient({
12    link: appLink,
13    cache: new InMemoryCache(),
14
15});

Summary

There are two types of errors that you can handle:

  1. 1. network errors

  2. 2. GraphQL error

There are three error policies (all, ignore, and none), and you can specify an error policy globally or on the operation level.

Moreover, there are two levels where you can handle those errors:

  1. 1. application level

  2. 2. component (query/mutation) level

Thanks to application-level error handling, you can use JavaScript error tracking tools on production and log errors to the console in local environments. Besides, you can use this mechanism to handle and display errors in your application.

Share this article with your friends!

Each share supports and motivates me to create new content. Thank you!

About the author

Marcin Kwiatkowski

Frontend developer, Certified Magento Full Stack Developer, writer, based in Poland. He has eight years of professional Software engineering experience. Privately, husband and father. He likes reading books, drawing, and walking through mountains.

Read more