はじめに
webシステムでデータの相互通信形式はREST APIやgRPCの他にGraphQLによる形式がある。
厳密にはGraphQLの通信形式はAPIとしての機構だが、クエリによってフロントエンド側が柔軟にデータを取得できる。
僕はREST API形式もGraphQL形式も扱ってきているが、GraphQLの優れている点として
- 各ページにおいてデータをまとめて取得することで、RESTのように何度も通信する必要がない(ケーニヒスベルクの橋の問題)
- ApolloやurqlなどのGraphQLクライアントとTypeScriptの型定義の組み合わせで強力なスキーマ駆動開発が可能
などが挙げられる。
自分も開発していて数多くの利点を感じている。
一方で僕の中ではGraphQL側が要求する技術的要件の難易度は高いとも思っている。
その理由を次に挙げる。
N+1問題と解決について
GraphQLとRDBMSは残念ながら相性の良い技術の組み合わせとは言えない。
データの捉え方に両者には隔たりがあるからだ。
GraphQLは木構造を辿りながら、データを捉えることで、データ取得の解決方法を提示している。
一方でデータベース(ここではRDBMSとする)ではテーブルを関連づけてデータを取得する解決方法を提示している。
この木構造を辿るという行為は、例えばユーザー一覧の中で投稿一覧を探索する際にそれぞれのリソースをユーザーのデータ分だけ探索してしまうことに繋がる。
これはデータ数が多くなればRDBMSの発行するSQLクエリの数が多くなってしまい、パフォーマンス面で非常に厄介な問題を引き起こす。
RDBMSに詳しいエンジニアの場合、これをテーブル結合で解決しようとするがGraphQLの「必要なデータのみクライアントが指定する」という考え方に背くことになる。
これは 先達者の記事 にもある通りである。
最もリーズナブルな解決方法としては記事にあるとおり、ORM層に自己解決させるというやり方だろう。(ID群を取得し、それをWHERE IN句でまとめて取得するやり方)
木構造と関係性データベース構造の難しさ
GraphQLとRDBMSではデータに対する捉え方が異なる。
この両者が一同に接する接合点でデータをどの粒度で捉えるか、は問題になる。
テーブルの粒度 = GraphQLのスキーマ単位
という考え方は厳密には適用できない、と個人的には考えている。
というより無理に押し込もうとすればするほど上手くいかない。
今のところ共通解は持ち合わせていないが僕の中ではまずGraphQLとして大きなグラフ構造を考えて粒度を決める、その次に必要なテーブルを作成する、というのが良いような気がする。
プロジェクトでGraphQLとREST APIどちらを使うべきか
まずプロジェクトが新規プロジェクトでデータ取得がある程度複雑になるプロジェクトであればGraphQLを採用するメリットはある。逆にいうとそれ以外のケースではお勧めしない。
例えば
- 既存プロジェクトのリライトやリファクタリング
- RDBMSの設計が初期段階で歪な構造になっている
こういう状況でGraphQLを採用すれば余計に問題を複雑にするだけである。
実体より手段を先行させないことが大事だと考える。