LoginSignup
15
11

More than 3 years have passed since last update.

Apollo ClientでSSRする時に知っておくといいかもしれないこと

Last updated at Posted at 2020-04-30

この記事は便宜上next.jsのexampleを例に出しているが、apollo clientでSSRしていればわかる内容になっているはず。

デフォルトのままでは使えない?

next.js公式のtypescript例を参考に実装を進めていると、データの取得とキャッシュ周りで、あまりうれしくない挙動をしていた。

うれしい挙動とは

apolloのキャッシュ機能をうまく使えば、サクサク動く、サーバー&データベースにやさしいスーパーサービスが作れるはず。
しかし、しんどさと複数タブのことを考えると、少なくともブラウザ上で動くサービスに関しては、以下のような挙動がうれしいのではないだろうか?

  • SSRしたときにデータを取得し、クライアントは送り付けられたデータをそのまま使う。
  • ページ移動→戻ってくるときにはもう一度データを取りにいく。

exampleの挙動とは

with-typescript-graphqlを動かしてみると、データ取得のタイミングは以下の通りになっている。

  • SSRしたときにデータを取得し、クライアントは常にそのデータを使う。

これはapollo clientのデフォルトの動きなので、別にexampleが悪いわけではないが、上記のうれしい挙動には程遠いものである。

どうすべきか

まず、クエリを投げる関数に、キャッシュを使わないよう、fetchPolicyオプションを渡す必要がある。
fetchPolicyのデフォルトの値はcache-firstであり、これはキャッシュがなければ(つまり初回)データを取りに行き、以後はキャッシュを使う、という設定だ。

これをcache-and-networkに変えよう。
cache-and-networkは、毎回新しいデータを取りに行き、更にキャッシュも活用してくれるという賢いやつだ。
「俺はキャッシュされるような、そんな安ぽい人間じゃない」と考えるストロングスタイルの方々は、network-onlyを使おう。

以下はreactの例である。
qiitaの上で直接書いたものだから多少間違えてるかもしれないが、雰囲気で分かるはずだ。

import { useQuery } from '@apollo/react-hooks';
export default () => {
  const {loading, error, data} = useQuery(QUERY, {fetchPolicy: 'cache-and-network'});
}

しかしこの方法ではいちいち指定することになってしまうのだが、当然デフォルト値を差し替えることもできるので、安心して下まで読んで欲しい。

現状確認

現在の挙動はこうなっているはずだ。

  • SSRしたときにデータを取得する
  • クライアントで表示した時、またデータを取得する。
  • ページ移動→戻ってくるときにはもう一度データを取りにいく。

お気づきだろうか? このままだとユーザーがページを開いたときに二回データ取得が発生してしまい、データベースが爆発するのだ。
二回目のデータ取得は全く意味がないので、これを何とかしよう。

因みにこの問題については2017年にissueが建てられているのだが、一度修正されてからまた再発したのかは知らないが、まったく解決される気配はないし、クローズされている。
https://github.com/apollographql/apollo-client/issues/2119

不要なリクエストを削減

new ApolloClient時に渡せる、ssrForceFetchDelayオプションを使おう。
このオプションは指定されたミリ秒間、fetchをせずキャッシュを使うというものだ。
これによって、クライアントにページが表示された直後の不要なデータ取得をなくすことができる。
セットする時間は公式のドキュメントに倣って、100にしておこう。

const client = new ApolloClient({
  cache: new InMemoryCache().restore(window.__APOLLO_STATE__),
  link,
  ssrForceFetchDelay: 100,
});

最終形態

どうすべきかの項で約束していたfetchPolicyのデフォルト値の差し替えを追加した最終形態がこれだ。

const client = new ApolloClient({
  cache: new InMemoryCache().restore(window.__APOLLO_STATE__),
  link,
  ssrForceFetchDelay: 100,
  // これでfetchPolicyのデフォルト値を変えられる
  defaultOptions: {
    watchQuery: {
      fetchPolicy: 'cache-and-network'
    },
  },
});

これであなたの

import { useQuery } from '@apollo/react-hooks';
export default () => {
  const {loading, error, data} = useQuery(QUERY);
}

  • SSRしたときにデータを取得し、クライアントは送り付けられたデータをそのまま使う。
  • ページ移動→戻ってくるときにはもう一度データを取りにいく。

になる。

15
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
11