DataLoader とは
GraphQL におけるデータ取得において内部で N+1 処理になるのを回避するためのツールです。例えば、あるリソース(A)一覧とそれに関連付けられた 1:1 のリソース(B)を一気に要求すること場合、最初のリソース(A)リストをクエリした後に、各レコードに対してリソース(B)を毎回クエリすると遅くなります。リソース(B)に対してそのキーをスタックしていき、まとめてクエリすることで N+1 回を 1+1 回に減らすことができます。この機能をまとめたものが DataLoader です。
実践的な使い方
条件検索について
DataLoader はあくまでも Key-value キャッシュ付きストアと考えた方がいいです。GraphQL の検索 Query で条件指定する場合は DataLoader を経由せずに直接行います。但し結果はその後の下階層で再度利用する可能性があるので prime メソッドでキャッシュしておきます。
キーによるレコード検索について
- マスターリソースに対する場合は、単純に主キーで取得およびキャッシュします。
- リレーションリソースに対する場合は、複合キーが cacheKeyFn の処理によってユニークな文字列になるように変換します。サロゲートキーは使いません。
要求したユーザーによって結果が変わる場合
この場合は、リクエストコンテキストに応じて DataLoader インスタンスを生成し付与します。コンテキストを生成するミドルウエア内で、DataLoader インスタンス自体をセットするか、それらインスタンスを生成できるメソッドを提供するといいでしょう。
どのレイヤーのデータを DataLoader で扱うべきか
GraphQL の Type とデータベースのレコードスキーマが完全に一致せずに、データの変換が必要となる場合の問題です。基本的にデータベースレイヤーに DataLoader を適用して、GraphQL Type には都度変換する方がいいと考えます。この理由としてリゾルバ周りの変更の影響を受けないことや、 N+1 はそもそもデータベース(ストレージ)への負荷であるからです。
コンテキストから DataLoader をどうモデルに渡すか
コンテキストはリゾルバのパラメータとして受け取りますが、DataLoader インスタンスは下位レイヤーのモデル(ロジック層やストレージ層)で使いたいので、そのままストレートにモデルのコンストラクタなどで渡すのが一番だと思います。これに引っ張られて Active-Record パターンは JavaScript ベースでは使いずらいと思います。Ruby on Rails 等だとスレッドに情報をセットして、透過的に扱うことも可能ですが。