【後編】Apollo Clientのキャッシュから考えるサーバー設計 | 株式会社トリドリ | 急成長企業を支援してきたマーケティング会社が厳選した急成長企業と出会える場所「LEAPLACE」 - LEAPLACE
LEAPLACE
アプリでより快適にご利用できます
アプリで開く
LEAPLACE

【後編】Apollo Clientのキャッシュから考えるサーバー設計

学び・ノウハウ共有
- 業務内容
見出し画像

※こちらの記事は後編です!前編も是非ご覧ください。



目次

  1. Mutationによるキャッシュの更新
  2. 1. 更新したフィールドがレスポンスに含まれていない
  3. 2. キャッシュされたコレクションの順序が変わる
  4. 3. Mutationが追加、削除の操作を行う
  5. まとめ
  6. おまけ
  7. 参考文献



Mutationによるキャッシュの更新

Mutationによってはキャッシュを更新できないことがあり、それを回避するには対策が必要です。
更新できないパターン⬇︎

  1. 更新したフィールドがレスポンスに含まれていない
  2. キャッシュされたコレクションの順序が変わる
  3. Mutationが追加、削除の操作を行う


1. 更新したフィールドがレスポンスに含まれていない

キャッシュはレスポンスを元に各オブジェクトを更新します。
そのため、更新したフィールドをレスポンスに含めなければなりません。
1つのMutationで複数オブジェクトのフィールドを更新する場合は、オブジェクトのリレーションを追加しサーバーから返却できるようにしましょう。


2. キャッシュされたコレクションの順序が変わる

実はキャッシュは配列内のオブジェクトの順序を保持しています。
しかし順序を入れ替えてたという情報も更新してしまうとキャッシュに反映できません。
これは、ROOT_QUERYのどのキーの配列が更新されたのかを知ることができないためと考えられます。
順序を入れ替えたオブジェクトを含む配列全体を再取得することで解決できます。
特にスキーマ設計で考慮する点はないと思いますが、知っておくと良いでしょう。


3. Mutationが追加、削除の操作を行う

ROOT_QUERYのどのコレクションが更新されたのかを知ることができないためと考えられます。追加が成功した後にクライアント側でキャッシュにも追加するという処理を記述してあげます。
キャッシュを更新するためにも、追加Mutationは追加したオブジェクトの情報を返してあげましょう。


▼addUser.ts

const [mutate, { data, error }] = useMutation< UserTypes.AddUser, UserTypes.AddUserVariables >( ADD_USER, { update (cache, { data }) { const newUserFromResponse = data?.addUser.user; const existingUsers = cache.readQuery<GetAllUsers>({ query: GET_ALL_USERS, }); if (existingUsers && newUserFromResponse) { cache.writeQuery({ query: GET_ALL_USERS, data: { users: [ ...existingUsers?.users, newUserFromResponse, ], }, }); } } } ) copy


削除の場合も追加と同じような理由からキャッシュが更新されません。
キャッシュを更新するにはクライアント側でキャッシュから対象のオブジェクトを削除します。
キャッシュ削除にはidから対象のオブジェクトを探し出しますので、削除Mutationのレスポンスとしては削除したオプジェクトのid一覧を返してあげましょう。



▼deleteUser.ts

const [mutate, { data, error }] = useMutation< DeleteUserTypes.DeleteUser, DeleteUserTypes.DeleteUserVariables >( DELETE_USER, { update (cache, el) { const deletedId = el.data?.deleteUser.user?.id const allUsers = cache.readQuery<GetAllUsers>({ query: GET_ALL_USERS }); cache.writeQuery({ query: GET_ALL_USERS, data: { users: allUsers?.users.filter((t) => t?.id !== deletedId) } }); cache.evict({ id: el.data?.deleteUser.user?.id }) } } ) copy



まとめ

Apollo Clientは取得したデータをインメモリキャッシュに正規化して自動的に保存します。
以下のようにサーバーを設計することで、キャッシュ機構の性能を発揮することができます。

  • idと__typenameを返却できるようにする。
  • 更新した項目を返却できるようにする。
    • 更新項目のフィールドをオブジェクトに追加
    • オブジェクトを跨ぐ場合は、リレーション先を追加
  • 更新Mutationのレスポンスとして、更新したオブジェクトを返却する
  • 削除Mutationのレスポンスとして、削除したオブジェクトのidを返却する



おまけ

バックエンドエンジニアもApollo Clientのキャッシュに興味を持ってもらえましたか?
実際にどうキャッシュされているか見てみたいという方もいるでしょう。
そんな方のためにApollo Client Devtoolsが提供されています。
ApolloClient初期化時にconnectToDevTools:trueにするとブラウザのdevtoolから確認できます。


▼Apollo Client Devtoolsを有効にする。

new ApolloClient({ uri: 'https://xxx.com', cache: new InMemoryCache(), connectToDevTools: true }); copy



参考文献

Demystifying Cache Normalization
Caching in Apollo Client
Developer tools


トリドリのプロダクト開発部では通年採用を実施しています! こちらを読んでみてください!


toridori開発部に興味がある方へtoridori初!新卒採用を開始します インフルエンサーマーケティング事業一本で急成長を続けているtoridori。今回toridoridevs.page.link