- GraphQL はデータの過剰取得問題を解決しようとするが、ほとんどのエンタープライズ環境ではすでに別の方法で解決済みの問題である
- BFF(Backend for Frontend) アーキテクチャが一般化した企業システムでは、GraphQL の主な利点は大きく薄れる
- 実装の複雑さ、可観測性の低下、キャッシュの問題、ID 制約、ファイル処理の不便さ などにより、実運用ではコストが大きくなる
- REST はシンプルで速く、エラー処理やオンボーディングが容易なため、大規模チーム環境でより効率的である
- 結論として GraphQL は 特定の状況では有用だが、ほとんどのエンタープライズには過剰な選択だと評価される
GraphQL が解決しようとする問題
- GraphQL の中核的な目的は overfetching(不要なデータの過剰取得) の防止
- クライアントが必要なフィールドだけを要求し、不要なデータ転送を減らす構造
- 新しい UI 要件が出るたびにバックエンドを修正する必要がないという利点
- しかし実際の環境では、この理想的な構造は 複雑な現実と合致しない
BFF で既に解決されている overfetching
- ほとんどのエンタープライズのフロントエンドは BFF(Backend for Frontend) レイヤーを使用している
- UI に合わせてデータを組み合わせ、複数のダウンストリーム呼び出しを統合し、バックエンドの複雑さを隠す
- REST ベースの BFF はすでに必要なデータだけを返せるため、GraphQL の利点が重複する
- GraphQL レイヤーが REST API からデータを取得する場合、overfetching は単に一段下へ移動するだけ
- 複数ページが同じエンドポイントを共有するときは GraphQL が有用な場合もあるが、
- その利点は 数キロバイト節約するために、より多くの設定と保守負担を受け入れる程度にとどまる
実装の複雑さと生産性の低下
- GraphQL は REST より 実装にずっと時間がかかり、複雑
- スキーマ、型、リゾルバ、データソース定義など追加作業が必要
- スキーマとクライアントの同期を維持する負担もある
- GraphQL は 利用(クライアントの利便性) を最適化する一方で、生産(サーバー開発速度) を犠牲にする
- エンタープライズ環境では 開発速度とシンプルさ のほうが重要である
可観測性とモニタリングの問題
- GraphQL の HTTP ステータスコード体系は一貫していない
- 200 レスポンスにもエラーが含まれうるため、モニタリング時に成功/失敗の区別が難しい
- REST は 2XX/4XX/5XX で明確に区分され、ダッシュボードのフィルタリングが直感的
- Apollo などでカスタマイズは可能だが、これは 追加設定と認知負荷 を招く
- 運用中の障害対応では、REST より 問題の把握が難しく複雑になる
キャッシュの現実的な限界
- Apollo の 正規化キャッシュ(normalized caching) は理論上は強力だが、実際には 脆く複雑
- フィールドが 1 つ違うだけのクエリも別物として扱われ、手動での関連付けが必要
- キャッシュのデバッグが独立した問題に発展する
- 一方 REST はレスポンス全体を単純にキャッシュでき、安定していて保守しやすい
ID フィールド制約の問題
- Apollo はすべてのオブジェクトに id または _id フィールド が必要だと仮定する
- 多くのエンタープライズ API には一意 ID が存在しないか、グローバル識別子ではない
- これに合わせるため、BFF が ローカル ID 生成ロジックを追加しなければならない
- 結果として 不要なフィールドとロジックが増え、overfetching 削減効果を相殺する
ファイルのアップロードとダウンロードの非効率
- GraphQL は バイナリデータ処理に不向き
- 実際にはダウンロード URL を返し、ファイル自体は REST で転送する
- PDF などの大容量データを GraphQL レスポンスに含めると 性能が低下する
- その結果、GraphQL の理想である「単一 API」は崩れる
オンボーディングと学習曲線
- ほとんどの開発者は REST の経験が豊富だが、GraphQL は学習が必要
- スキーマ、リゾルバ、クエリ構成、キャッシュ規則、エラー処理など新しい概念を学ぶ必要がある
- そのため チームのオンボーディング速度が低下する
- REST は「退屈だがスケーラブルな」アプローチとして、大規模チームに適している
エラー処理の複雑さ
- GraphQL のエラーレスポンスは nullable フィールド、partial data、errors 配列、拡張ステータスコード などで複雑
- REST は単純に 400/500 で区別され、理解とデバッグが容易
結論: GraphQL はニッチ技術
- GraphQL は 特定の状況では有効なツール
- しかし大半のエンタープライズ環境では、すでに BFF と REST で問題を解決している
- 主要な課題は overfetching ではなく、可観測性、信頼性、速度 である
- 結果として GraphQL は 狭い問題を解決する一方で、より広い複雑性を招く
- 結論は「GraphQL は悪くないが、ほとんどの場合は必要ない」というもの
2件のコメント
長所と短所はあると思いますが、スキーマレベルでデータとアクセス制御を設計して、何かを1つ追加するたびに REST API に追加し、あとで全部返すようにするよりはよく見えます。欠点は明確ですが、長所も明確なので(笑)
Hacker Newsの意見
GraphQLの主な問題をoverfetchingだと規定する記事には同意しない
私が見るに本当の利点は、(a) 厳格な型ベースの契約を強制できること、(b) スキーマ進化がはるかに容易なこと
型システムのおかげで入力と出力は常に定義された形に従い、カスタムスカラー型(例: 電話番号、メールアドレスなど)を使えばバグやセキュリティ問題を大幅に減らせる
また、新しいフィールドの追加や既存フィールドの廃止が標準化されているため、サーバー・クライアントの両方で認知負荷が小さい
私がGraphQLを使う理由はAPIの組み合わせと進化にある。特にM:N構造の大規模システムでは、「クライアントが必要なものを記述 → サーバーが組み合わせる → ドメインサービスが解決する」という流れのほうが、長期的にはるかに管理しやすい
優れたobservabilityと組み合わせれば、データアクセスの強力な基盤になる
もう一つはresolverの再利用とfederationが容易なこと。RESTではこれはかなり面倒だ
スキーマ進化はProtobufでもうまく扱える
GraphQLの本当の利点は、UIデータを小さな断片として組み合わせられることだ
この動画のようにfragment colocationを活用すれば、下位コンポーネントを修正しても他の部分に影響しない
クエリがfragmentベースで自動生成されるので、フィールド削除時に他のコンポーネントが壊れるリスクも減る
規模が小さい、あるいは高速な開発が重要でないなら、GraphQLは過剰投資に見えるかもしれない
Colocationは本当に革新的な概念なのに、Apolloはこれをほとんど語らなかった
Relayのドキュメントは依然として不十分だが、Entrypointという概念は素晴らしい
ただしgraphql-codegenの実装はプラグイン互換性が低く、私たちは自前でプラグインを作らなければならなかった
エコシステム全体の一貫性が不足している
overfetchingがGraphQLの核心的な問題だというのは誇張だ
GraphQLは、ORMが解決するインピーダンスミスマッチをクライアントレベルで扱うための道具だと考えている
Relayのようなコンパイラベースのツール群なしで使うのはアンチパターンだ
最近はAIがデータレイヤーを自動生成してくれるので、GraphQLの必要性は下がっているように見える
存在しないフィールドを選択すると自動生成してくれるなど、開発体験(DevEx) が優れている
現代のWebが遅くなっている主因はほとんどがデータの過剰転送だ。実際、多くのアプリは0.5%未満の効率で動いている
私は2016年からGraphQLを使ってきた
本質的にGraphQLはRPC仕様だ。サーバーの「Action(Args) → ResultType」マップとして実装される形になる
RESTの複数エンドポイントの代わりに、GraphQLは単一の
/queryエンドポイントでresolverマップを通じて動作する結局はOpenAPIやgRPCのように入出力が型で定義された構造だ
Apolloがfragment maskingを追加して多少は良くなったが、それでもRelay中心の考え方が重要だ
バックエンドがこれを単一のSQLクエリに変換して処理する時に最も効率的だ
resolverはDBから直接取得できないデータに対してのみ例外的に使うべきだ
以前チームを率いていた時、FEがGraphQLを望んだので導入した
結果として各ページごとに1つの巨大なクエリで全データを取得し、修正時にはJSON blob全体を送り返していた
アプリは動いていたが、結局会社がピボットしたことでそのコードは消えた
実際、多くのGraphQLプロジェクトはこうした形式だけ整った実装で終わるように思う
GraphQLの認証(auth)フローは最大の難題の一つだ
resolverがさまざまなコンテキストで呼ばれるため、あらゆるケースを考慮しなければならない
複雑さと認知コストが大きすぎて、結局フィールドをロックすることになる
自分でgraphql-autharooを作ったが十分ではなかった
たいていの機能はGraphQLなしでももっとシンプルに実装できる
GraphQLリソースごとに細かな権限を追加しなければならず、リソース全体が更新されるまでクエリが動作しない
「RESTで作り直したほうがよい」という声が出るほど負担が大きい
自前で組み合わせるより、完成度の高いサーバーフレームワークを使うほうがよいと思う
GraphQLは見た目は良さそうだが、実際には時間が経つほど保守が難しくなる技術だ
多くの技術がそうであるように、結局車輪はただの車輪にすぎないという教訓を与えてくれる
GraphQLの利点は、スキーマにフィールドを追加するだけで全クライアントがすぐにクエリできることだ
「このフィールドも追加してほしい」というFEからの要望がなくなり、協業がシンプルになる
また、スキーマスナップショットを作って統合テストで変更検知するのも容易だ
RESTより一貫性が高いと感じたが、これは個人的な経験だ
AからEまで連鎖的に要求できるので、性能低下やフロントエンドとデータ構造の結合が深刻になる
Reactも今ではフレームワークベースのSSRとサーバーコンポーネントが前提になっている
結局、TypeScriptがクライアントとサーバーを統合する方向に進んでいる
GraphQLがDB負荷や非効率なクエリの問題をどう防ぐのか気になる
悪意あるクエリが内部状態を爆発させることもあるのではないか?
RESTがあまりに退屈なのでGraphQLを試している
サーバーとクライアントがリクエスト・レスポンスについてコンパイル時に合意できる点が気に入っている
ブログには「これはよくない」と言うだけでなく、代替技術も一緒に示してほしい
どちらの技術もクライアント・サーバー契約の問題をうまく解決する