Apollo Client のエラーポリシーやApollo Linkでエラーハンドリングする

GrahqQLサーバーではさまざまな種類のエラーが発生します。Apollo Clientはサーバーでエラーが発生した時にエラーハンドリングをエラーポリシーやApollo Linkによって制御することができます。

エラーの種類

  • GraphQLエラー: シンタックスエラー、バリデーションエラー、リゾルバーエラーなどGraphQLのクエリに起因するエラーです。
  • ネットワークエラー: サーバーとのネットワークやサーバーの状態に起因するエラーです。ステータスとしては、4xxや5xxなどのステータスコードが返ってくるようなエラーです。

GraphQLエラー

GraphQLエラーには、次のようなエラーが含まれます。シンタックスエラーとバリデーションエラーはサーバー上でGraphQLクエリは実行されませんが、リゾルバーエラーはサーバー上でクエリは実行されて部分的なデータを返すことが可能です。

  • シンタックスエラー:クエリの文法が間違っているなど
  • バリデーションエラー:クエリにスキーマに存在しないフィールドが指定されているなど
  • ゾルバーエラー:リゾルバーの処理を実行時にエラーが発生した場合など
// シンタックスエラーのレスポンス例
{
  "errors": [
    {
      "message": "Cannot query field \"authoraaa\" on type \"Book\". Did you mean \"author\"?",
      "locations": [ { "line": 5, "column": 5 }],
      "extensions": {
        "code": "GRAPHQL_VALIDATION_FAILED",
        "stacktrace": [
          "GraphQLError: Cannot query field \"authoraaa\" on type \"Book\". Did you mean \"author\"?",
          "...",
        ]
      }
    }
  ]
}

ネットワークエラー

ネットワークエラーは、サーバーとの通信時のネットワークやサーバーの状態に起因するエラー(ステータスとしては、4xxや5xxなど)です。ネットワークエラーが発生すると、error.networkErrorにエラー内容が設定されます。

Apollo Clientのエラーポリシーの種類

GraphQLのリゾルバーエラーが発生したときは、レスポンスはerrorsにエラー内容が含まれます。また、dataに部分的なエラーが含まれる可能性があります。

// リゾルバーエラーのレスポンス例
// エラー時にもdataに値が部分的に含まれている
{
  "errors": [
    {
      "message": "Invalid argument value",
      "locations": [ ... ],
      "path": [ ... ],
      "extensions": {
        "code": "BAD_INPUT",
      }
    }
  ],
  "data": {
    "books": [
      {
        "id": 1,
        "title": "The Awakening",
      },
    ],
    "failedBook": null
  }
}

エラーポリシーを設定することで、エラー時のデータの取り扱いを制御することができます。

エラーポリシー 説明 errorの値 dataの値
none(デフォルト) エラーのみ表示 error.graphQLErrorsにエラーを設定 undefined
ignore エラーが起きてないように振る舞う undefined 部分的なデータが設定
all 部分的なデータとエラーの両方をレンダリングできる error.graphQLErrorsにエラーを設定 部分的なデータを設定
// 'all'の場合の例
const { loading, error, data } = useQuery(GET_BOOKS, {
  errorPolicy: 'all',
});

if (!loading) {
  console.log(data); //=> 部分的なデータが存在
  console.log(error); //=> ApolloError
  console.log(error.graphQLErrors) //=> GraphQLのエラーが設定
}

詳細は、GraphQL error policiesを参照ください。

Apollo Clientのエラーポリシーの設定方法

エラーポリシーを設定するには、主に次の2つの方法があります

  • グローバルにエラーポリシーを設定
  • useQueryなどのオペレーション時にエラーポリシーを設定

グローバルにエラーポリシーを設定

ApolloClientの初期化時にdefaultOptionsからエラーポリシーを設定することができます。

import { ApolloClient, InMemoryCache } from '@apollo/client';

const client = new ApolloClient({
  cache: new InMemoryCache(),
  uri: 'http://localhost:4000/',
  defaultOptions: {
    query: {
      errorPolicy: 'all',
    },
    mutate: {
      errorPolicy: 'ignore',
    },
  },
});

参考: class Apollo Client

オペレーション時にエラーポリシーを設定

useQueryなどのフックのオプションにエラーポリシーを設定することで、個別にエラーポリシーを設定することができます。

const { loading, error, data } = useQuery(GET_TODOS, {
  // 個別のクエリでerrorPolicyを指定
  errorPolicy: 'all',
});

Apollo Linkで高度なエラーハンドリングをする

Apollo Link ライブラリを使うことで、高度なエラーハンドリングを実現できます。

Apollo Linkは、Apollo ClientがGraphQLサーバーと通信をする間のデータの流れを制御することができます。 Apollo ClientはデフォルトでHttpLinkを利用しており、HTTPでGraphQLサーバーにデータを通信する制御をしています。

Apollo Linkの概要(引用元: https://www.apollographql.com/

onErrorリンクを追加することで、エラー内容に基づいて処理を実行することができます。

import { ApolloClient, InMemoryCache, HttpLink, from } from "@apollo/client";
import { onError } from "@apollo/client/link/error";

// HttpLinkはGraphQLサーバーとHTTPでやり取りするために必要
const httpLink = new HttpLink({
  uri: "http://localhost:4000/graphql"
});

// onErrorはGraphQLサーバーからのレスポンスのgraphQLErrorsかnetworkErrorをハンドリングできる
const errorLink = onError(({ graphQLErrors, networkError }) => {
  if (graphQLErrors)
    graphQLErrors.forEach(({ message, locations, path }) =>
      console.log(
        `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`,
      ),
    );

  if (networkError) console.log(`[Network error]: ${networkError}`);
});

const client = new ApolloClient({
  // linkオプションで定義したApollo Linkをチェインさせる
  link: from([errorLink, httpLink]),
  cache: new InMemoryCache()
});

Apollo Linkについてより詳しく知りたい場合は、Apollo Link Overviewを参照してください。