Apolloクライアントには、キャッシュ機能が備わっています。
使い方は、公式載っています。
これに絡んでちょっとはまってしまったので、メモとして残します。
はまったこと
以下のような現象が発生しました。
- Apolloサーバー(NestJS)から、データベースに登録されているスタッフの一覧を取得する
- スタッフを1名追加するmutationを呼び出し、データベースに追加する
- 再度、Apolloサーバーから、スタッフ一覧を取得する
この結果、どうなると思いますか?
mutationで追加した後に、スタッフ一覧を取得しても、追加したはずのスタッフがいないんです。
なぜこんなことが起きるのか?
調べて見て、分かったことは以下の通りです。
- Apolloクライアント側のスタッフ一覧は正常終了している
- 正常終了しているものの、追加したスタッフは入っていない
- ページを再ロードすると追加したスタッフが表示される
- Apolloサーバーのログを見ると、スタッフ追加後にスタッフ一覧を取得した形跡がない(SQLが発行されていない)
これらのことから、「Apolloクライアントにはキャッシュ機能があって、何らかの要因でキャッシュが有効になっているのではないか?」という結論に至りました。
英語が不得意なので、正確には読み取れていませんが、__typenameとidのデータを見てキャッシュを構成しているようです。mutationした場合に、__typenameとidを返すようにしていれば、キャッシュが更新されるようです。
しかし、私の場合、追加した結果は別の形で返しているので、この機能が働かず古いキャッシュ情報が返って来ていたと言う事のようです。
どうするか?
とは言え、キャッシュが正しく機能するように改修するのは、時間がかかります。
何か、方法はないかと公式を見てみるとありました。
mutationでスタッフを追加するときに、強制的にスタッフ一覧を取得するという方法です。
save(_staff: User): Promise<ISaveStaffResult> {
return new Promise((resolve, reject) => {
this.apollo.mutate({
mutation: this.saveStaffMutation,
variables: {
staff: _staff
}
})
.subscribe(( {data} ) => {
const ret: any = data;
resolve(ret.savestaff);
}, (err) => {
reject(err);
});
});
}
このようにmutationを呼び出すときに、refetchQueriesを追加します。
save(_staff: User): Promise<ISaveStaffResult> {
return new Promise((resolve, reject) => {
this.apollo.mutate({
mutation: this.saveStaffMutation,
variables: {
staff: _staff
},
// 追加するのはこれ!!
refetchQueries: [{
query: this.getStaffAllQuery
}],
awaitRefetchQueries: true
})
.subscribe(( {data} ) => {
const ret: any = data;
resolve(ret.savestaff);
}, (err) => {
reject(err);
});
});
}
こうすると、mutationが完了した後に、スタッフ一覧を取得するリクエストがApolloサーバーに飛んで行きます。