nipeblog

Apollo Client のキャッシュの仕組みメモ

Apolloクライアントのキャッシュの仕組みをメモがてらに記載しています。

Apollo Client のキャッシュの概要

Apollo クライアントは、GraphQLクエリの結果を正規化してローカルのメモリ内に保存します。このメモリ内のクエリ結果をキャッシュとして利用します。

GraphQLクエリの実行時にクエリ結果がキャッシュ(メモリ内)にある場合は、ネットワークリクエストを省略して即時に応答することでパフォーマンス向上ができます。

公式ドキュメントのシーケンス図を引用しつつ、具体例を説明します。

GetBook(bookId: "5")のGraphQLクエリを実行するケースを考えてみましょう。

「Apollo Client」の初回のGetBook(bookId: "5")のGraphQLクエリ実行時は「InMemoryCache」にBook:5のデータが存在しないため、「GraphQL Server」にクエリを実行して結果を取得します。そして、クエリ結果を「InMemoryCache」に正規化して保存し、「Apollo Client」にクエリ結果を返します。

初回リクエスト時のシーケンス図(引用元:https://www.apollographql.com/)

その後、2回目以降のGetBook(bookId: "5")のGraphQLクエリを実行時は、「InMemoryCache」にBook:5のデータが存在するため、「GraphQL Server」にネットワークリクエストを実施せず、そのままキャッシュの値を「Apollo Client」に返します。

2回目以降のリクエスト時のシーケンス図(引用元: https://www.apollographql.com/)

このように、キャッシュを使うことで、アプリケーションは即時に応答することができます。

しかし、キャッシュを使うことでメモリとサーバーの状態がずれてバグの温床にもなり得るので注意が必要です。

サポートされているFetch Policy

Apollo Client でサポートされているフェッチポリシーは次の6つになります。キャッシュの挙動に影響を与えるため、どのポリシーを使っているかを理解することが大切です。

ポリシー名

説明

cache-first (デフォルト)

ネットワークリクエストの数を最小限に抑えることを優先している。
キャッシュに全てのフィールドのデータがあれば返す。なければGraphQLサーバーにクエリをリクエストする。クエリ結果をキャッシュする。

cache-and-network

高速な応答を提供しつつ、サーバーとローカルデータの一貫性を維持できる。
キャッシュとGraphQLサーバーの両方にクエリを実行し、サーバ側のクエリ結果をキャッシュする。

network-only

サーバーとローカルデータの一貫性を常に保てる。
キャッシュを参照せず、GraphQLサーバーに常にリクエストする。クエリ結果はキャッシュする。

no-cache

サーバーとローカルデータの一貫性を常に保つが、キャッシュは更新しない。
キャッシュを参照せず、GraphQLサーバーに常にリクエストする。クエリ結果はキャッシュしない。

cache-only

キャッシュに全てのフィールドのデータがあれば返す。なければ、エラーをスローする。

standby

cache-firstと同じロジックだが、フィールドが更新されても自動的に更新しない。

Fetch Policyの設定方法

フェッチポリシーの設定方法は、主に次の2つがあります。

  • new ApolloClient時にfetchPolicyを指定
  • useQueryfetchPolicy を指定

new ApolloClient時にfetchPolicyを指定」するとデフォルトのフェッチポリシーを指定することができます。(参考: class Apollo Client

const client = new ApolloClient({
  uri: 'https://flyby-router-demo.herokuapp.com/',
  cache: new InMemoryCache(),
  defaultOptions: {
    query: {
      fetchPolicy: 'cache-first',
    },
    watchQuery: {
      fetchPolicy: 'cache-and-network'
    },
  },
});

また、「useQueryfetchPolicy を指定」すると、個別でフェッチポリシーを指定できます。

const { loading, error, data } = useQuery(GET_TODOS, {
  fetchPolicy: 'network-only', // キャッシュを利用せずGraphQLサーバーに常にリクエスト。クエリ結果はキャッシュする
});

Apollo Client のキャッシュを可視化する

ブラウザ拡張の Apollo Client Devtools を利用することで、キャッシュを可視化できます。

Apollo Clientのキャッシュの仕組み

キャッシュはメモリに正規化されて格納されます。

また、キャッシュのIDはデフォルトでは __typenameid (もしくは _id )フィールドを使います。

詳細を知りたい場合は、How is data stored? で確認できます。

Apollo Client のキャッシュを操作する

Apollo Client のキャッシュを直接操作することができます。

メソッド

説明

readQuery / writeQuery / updateQuery

GraphQLのクエリ結果を取得・変更できる

readFragment / writeFragment / updateFragment / useFragment

フラグメントの値を取得・変更できる

cache.modify

キャッシュの値を直接変更できる

より詳細は Reading and writing data to the cacheを参照ください。