Apollo Client Error Handling

Introduction

In this article, we would learn and clarify error handling with Apollo in React. The error handling occurs on two levels:

  • The application level and
  • The query or mutation level.

Both may be applied with the two cases that follow. We have access to the query data and loading properties on a query level, in our Profile component. We may similarly access the error object that can be used to show a conditional error message apart from these.

Description

Apollo Client may face a variety of errors when executing operations on the GraphQL server. It supports us to handle these errors according to their type. That allows us to show suitable information to the user when an error occurs. Performing GraphQL operations on a remote server may yield GraphQL errors and network errors.

GraphQL errors

GraphQL errors appear in the server-side execution of a GraphQL operation. They comprise:

  • Syntax errors,  for example, a query was malformed.
  • Validation errors, for instance, a query comprised a schema field that doesn’t take place.
  • Resolver errors, for example, an error that happens during attempting to populate a query field.

Our server doesn’t perform the operation at all as it’s invalid if a syntax error or validation error occurs. Our server may still return partial data if resolver errors happen. Our server contains it in the errors an array of its response to Apollo Client if a GraphQL error occurs.

Apollo Client then adds those errors to the error.graphQLErrors array. That is returned by the useQuery call. Apollo server replies with a 4xx status code if a GraphQL error stops Apollo Server from performing the operation at all.  Apollo Server gives a response with a 200 status code if resolver errors happen. Though the response still includes partial data.

An operation that outcomes resolver errors may also return partial data. This means that some of the data in our operation requested are added in the server’s response. Apollo Client refuses partial data by default. We may override this behavior with the setting of a GraphQL error policy.

Network errors

Network errors were encountered during attempting to communicate with the GraphQL server. These errors are usually resulting in a 4xx or 5xx response status code and no data. Apollo Client delivers it to the error.networkError field returned by the calling when a network error occurs. By handling the application with Apollo Link we may include retry logic and other advanced network error.

GraphQL error policies

Our server’s response might yet add partial data in the data field if a GraphQL operation yields one or more resolver errors.

{
  "data": {
    "getInt": 12,
    "getString": null
  },
  "errors": [
    {
      "message": "Failed to get string!",
      // ...additional fields...
    }
  ]
}

Apollo Client gets rid of partial data. It populates the error.graphQLErrors array of the useQuery call by default. We can in spite of using these partial results by defining an error policy for our operation.

The below error policies are being supported by the Apollo Client for an operation:

POLICY DESCRIPTION
none This is the default error policy. They are returned on error.graphQLErrors. The response is set to even if the server returns in its response as if the response includes GraphQL errors. This means network and GraphQL errors result in the same way response shape.
ignore graphQLErrors are ignored.
all data and error.graphQLErrors are together populated. That allows us to render both partial results and error information.

Error policy setting

Mention an error policy in the options object we provide our operation hook for example useQuery), similarly as:

const MY_QUERY = gql`
  query WillFail {
    badField # This field's resolver yields an error
    goodField # This field is populated easily
  }
`;

function ShowingSomeErrors() {
  const { loading, error, data } = useQuery(MY_QUERY, { errorPolicy: 'all' });
  if (loading) return <span>loading...</span>
  return (
    <div>
      <h2>Good: {data.goodField}</h2>
      <pre>Bad: {error.graphQLErrors.map(({ message }, i) => (
        <span key={i}>{message}</span>
      ))}
      </pre>
    </div>
  );
}

This instance uses the all error policy to render together partial data and error information whenever applicable.

The Apollo Link library allows us to configure advanced handling of errors that happen during performing GraphQL operations. We may add a link to the link chain that receives error details and acts on them accordingly. The example below passes the ApolloClient constructor a link chain with two links:

  • An onError the link that checks for graphQLErrors or a networkError in the server’s response. It logs the details of whichever error(s) it finds.
  • An HttpLink that sends each GraphQL operation to our server.

Retrying operations

Apollo Link supports us to retry failed operations, which might be solved by a follow-up attempt. We suggest diverse links depending on the type of error that happened:

  • The onError link for GraphQL errors
  • The RetryLink for network errors

On GraphQL errors

The onError the link may retry a failed operation based on the type of GraphQL error that’s returned. For instance, when using token-based authentication, we might desire to automatically handle re-authentication when the token expires. We return forward(operation) in our onError function to retry an operation. Look at this example:

onError(({ graphQLErrors, networkError, operation, forward }) => {
  if (graphQLErrors) {
    for (let err of graphQLErrors) {
      switch (err.extensions.code) {
        // Apollo Server adjusts code to UNAUTHENTICATED
        // when an AuthenticationError is thrown in a resolver
        case 'UNAUTHENTICATED':

          // Modify the operation context with a new token
          const oldHeaders = operation.getContext().headers;
          operation.setContext({
            headers: {
              ...oldHeaders,
              authorization: getNewToken(),
            },
          });
          // Retry the request, returning the new observable
          return forward(operation);      }
    }
  }

  // To retry on network errors, we recommend the RetryLink
  // instead of the onError link. This just logs the error.
  if (networkError) {
    console.log(`[Network error]: ${networkError}`);
  }
});

For more details visit: https://www.technologiesinindustry4.com