キカガク プラットフォームブログ

株式会社キカガクのプラットフォームブログです。エンジニアやデザイナー、プロダクトマネージャーなどが記事を書いています。

Apollo Client の Cache 入門

はじめに

プラットフォーム部の茂木です。普段は、キカガク for Business の開発を担当しています。

私の所属する「キカガク for Business 開発チーム」では、2週間に1度、知見を自由に共有できる勉強会を実施しています。

※「キカガク for Business 開発チーム」内での勉強会についてはこちらをご覧ください!

その中で、私は「Apollo Client の Cache」をテーマとして発表する機会が何度かあったため、そのまとめ記事を執筆したいと思いました。

というわけで今回は、Apollo Client での Cache の仕組みとその実装方法について紹介します。

対象読者

  • GraphQL の導入で、Apollo Client での Cache を利用したいという方
  • Apollo Client での Cache の仕組みについて、具体例を交えつつ理解したい方

そもそも Apollo Client とは

Apollo Client とは、JavaScript で GraphQL を使用するためのオープンソースライブラリのことを指します。

次の画像のように、アプリケーションにて GraphQL で作成されたクエリを元にリクエストを作成し、GraphQL エンドポイントとのやりくりをしてくれます。

Apollo Client では、リクエストとレスポンスのやりくりをしているので、パフォーマンスの向上のため、Cache も利用することが可能です。この Cache の保存では、独自の flat lookup table という方式が採用されています。

Cache の保存はどんな仕組みなのか

flat lookup table とは、データに一意な Id を設定しながら正規化し、単一でも複合でも呼び出せるように保存する方法です。

具体的には、まず次のようなクエリとそのレスポンスを考えます。

送信するクエリ

query{
  users{
      userId
      name
  }
}

レスポンス

{
  "data":{
    "users": [
      {
        "__typename": "User",
        "userId": "cGVvcGxlOjEt",
        "name": "user_1",
      },
      {
        "__typename": "User",
        "userId": "xDrKcTt1lWdh",
        "name": "user_2",
      }
    ]
  }
}

これに対し、まずは Object を識別し、データを正規化します。

{
  "__typename": "User",
  "userId": "cGVvcGxlOjEt",
  "name": "user_1",
}
{
  "__typename": "User",
  "userId": "xDrKcTt1lWdh",
  "name": "user_2",
}

今回は User に関して、2つのデータがありました。次は、これらに対して一意となる Id を生成します。今回のケースはそれぞれ userId を一意な Id とすれば、そのまま Cache の Id として利用できます。

__ref:"User:{"userId":"cGVvcGxlOjEt"}" // user_1のCacheId
__ref:"User:{"userId":"xDrKcTt1lWdh"}" // user_2のCacheId

この Id を最初のクエリのレスポンスに当てはめることで、下記のようなクエリの Cache と User データの Cache がそれぞれ保存されることになります。

query{
  users:{
    users: [
      __ref:"User:{"userId":"cGVvcGxlOjEt"}",
      __ref:"User:{"userId":"xDrKcTt1lWdh"}"
    ]
  }
}

Cache を利用するための実装方法

実装するのは非常に簡単で、Apollo Client のコンストラクタに InMemoryCache を設定するだけで、利用可能となります。

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

const client = new Apollo Client({
  // ...other arguments...
  cache: new InMemoryCache(options)
});

この InMemoryCache には、option が 設定でき、typePolicies という設定を使うことで、Cache する際の一意な Id をデータ内の任意の Id に指定することも可能です。

※特に何も設定しない場合でも、id または _id フィールドが含まれていれば、自動でその値を Id として利用してくれます。

例えば Cache の 一意な Id を任意の Id で設定したい場合は、次のように実装します。

const cache = new InMemoryCache({
  typePolicies: {
    User: {
      IdFields: ["userId"],
    },
  },
});

ここで、先述した手順では

それぞれ userId を一意な Id とすれば、そのまま Cache の Id として利用できます。

と書きましたが、今回例に挙げたデータでは、typePolicies が設定されていない場合は、自動で userId を一意な Id として設定できなくなります。その場合、Cache を利用する際にデータを特定できず、エラーが発生する可能性がありますのでご注意ください。

おわりに

今回は、Apollo Client の Cache の仕組みとその実装方法を紹介しました。

Cache をうまく利用することでリクエストを減らせば、通信量を減らせる、画面描画を早くできるなどのメリットがあります。

GraphQL 導入時の Cache 設定について調べていた方の参考になれば幸いです。

参考