GraphQL gRPC徹底比較
RESTの次世代を選ぶ「適材適所」のAPI設計論
前回の記事で、我々は「REST API」の理想と現実を解き明かした。多くの現場で使われる「REST API」が、HTTPメソッドとURI(リソース)の原則を採用しつつも、提唱者が目指したHATEOASという理想には至っていない現実を見た。
そして、その現実的なREST(HTTP+JSON API)には、 「N+1問題」 や 「オーバーフェッチ」 といった、クライアント開発(特にモバイル)において深刻なパフォーマンス問題を引き起こす限界があることも指摘した。
このRESTの限界を乗り越えるべく登場したのが、Facebook(現Meta)が生んだ GraphQL と、Googleが生んだ gRPC である。
だが、これら二つの技術を「RESTの次」として単純に比較し、「どちらが優れているか」を競わせるのは危険な誤解を生む。GraphQLが解決しようとした課題と、gRPCが解決しようとした課題は、似ているようで根本的に異なるからだ。
GraphQLは「データ取得の柔軟性」を、gRPCは「通信の絶対速度」を追求した技術である。
本記事の目的は、この二つの技術の設計思想、具体的な仕組み、そして得意分野を徹底的に対比することだ。この記事を読み終えるとき、読者は「いつ、どちらの技術(あるいは引き続きREST)を選ぶべきか」という「適材適所」の判断基準を明確に手に入れているはずである。
1. GraphQL:「クライアントの要求」にすべて応えるクエリ言語
GraphQLは、APIのための「クエリ言語(Query Language)」である。その最大の特徴は、 「必要なデータ構造をクライアント側が定義して要求する」 という点にある。これは、サーバー側が定義したリソース(URI)にアクセスするRESTとは根本的に異なる設計思想だ。
GraphQLが解決する核心的課題
GraphQLが狙い撃ちしたのは、まさしくRESTが抱える以下の問題である。
- オーバーフェッチ(Over-fetching): 「名前だけ欲しい」のに、RESTの
GET /users/123が住所や電話番号まで返してしまう問題。 - アンダーフェッチ(Under-fetching): 1つの画面(例:ユーザー情報+最新投稿3件)を表示するために、
GET /users/123とGET /users/123/postsのように複数回のAPIリクエストが必要になる問題。これはN+1問題の温床でもある。
これらの問題は、クライアント(特に通信環境が不安定なモバイルアプリ)の表示速度やUX(ユーザー体験)を著しく低下させる。
GraphQLの仕組み:要求した通りのデータが返る
GraphQLでは、クライアントは「このような構造のJSONが欲しい」というクエリをサーバー(通常は単一のエンドポイント、例: /graphql)にPOSTで送信する。
例:RESTとGraphQLの対比
ある画面で「ID:123のユーザー名」と「そのユーザーの最新投稿2件のタイトル」が必要だとする。
RESTの場合(アンダーフェッチ発生)
- Request 1:
GET /users/123
- Response:
{"id": 123, "name": "Yamada", "age": 30, "address": "...", ...}- クライアントは “name” 以外を捨てる(オーバーフェッチ)
- Request 2:
GET /users/123/posts?limit=2
- Response:
[{"id": 501, "title": "First post", "body": "..."}, {"id": 502, "title": "Second post", "body": "..."}]- クライアントは “title” 以外を捨てる(オーバーフェッチ)
このように、2回のリクエストと、大量の不要データ(オーバーフェッチ)が発生する。
GraphQLの場合(1リクエストで解決)
- Request:
POST /graphql
- Query (Body):
- Response:
たった1回のリクエストで、クライアントが要求した構造(
nameとpostsのtitle)通りのデータが、過不足なく返却される。オーバーフェッチもアンダーフェッチも存在しない。
GraphQLの主戦場とトレードオフ
この特性から、GraphQLの主戦場は明確である。
- 主戦場: クライアント-サーバー間通信。
- 特に、Webフロントエンドやモバイルアプリなど、多様な画面構成やデータ要求が頻繁に変わる場所。
- BFF (Backends For Frontends) パターン、すなわちフロントエンドごとに最適化されたAPI層として絶大な効果を発揮する。
ただし、GraphQLには強力な反面、考慮すべきトレードオフが存在する。
- サーバー側の複雑性: サーバーはクライアントのどんなクエリにも応えられるよう、データの関連性を解決するロジック(リゾルバ (Resolver) と呼ばれる)を細かく実装する必要があり、RESTより複雑になる。
- キャッシュ戦略: RESTのようにURIごと(
GET /users/123)にHTTPキャッシュ(CDNなど)を効かせるのが難しくなる(すべてPOST /graphqlになるため)。 - クエリの暴走: クライアントが過度にネストした複雑なクエリ(例:ユーザーの友達の友達の投稿一覧…)を投げると、サーバーに過負荷がかかる危険性がある(対策は可能)。
2. gRPC:「サーバー間の速度」を極限まで追求するRPC
gRPCは、Googleが開発したオープンソースの「RPC (Remote Procedure Call)」フレームワークである。RPCとは、日本語で「遠隔手続き呼出」と言い、まるでローカルにある関数(手続き)を呼び出すかのように、ネットワーク越しに別サーバーの機能を呼び出す仕組みを指す。
gRPCは「RESTの限界」を解決するために生まれたが、GraphQLとは全く異なるアプローチを取った。gRPCが着目したのは、 「通信の絶対速度」 である。
gRPCが解決する核心的課題
gRPCが狙い撃ちしたのは、RESTが前提とする「HTTP/1.1」と「JSON」が持つ、通信効率の悪さである。
- HTTP/1.1のオーバーヘッド: リクエストごとに接続を確立し直す(あるいは接続数に限りがある)ため、非効率。
- JSONの冗長性: JSONは人間が読めるテキスト形式であり、非常に便利だが、バイナリ形式に比べてデータサイズが大きく、解析(パース)にも時間がかかる。
これらの問題は、クライアント-サーバー間でも影響するが、それ以上に、システム内部でサーバー同士が1秒間に何千回、何万回と通信し合う「マイクロサービス」環境において致命的なボトルネックとなる。
gRPCの仕組み:HTTP/2とProtobufによる高速化
gRPCは、速度を追求するために2つの強力な技術を土台としている。
- HTTP/2:
HTTP/1.1の欠点を根本的に改善したプロトコル。gRPCはHTTP/2を必須とする。
- 単一接続の多重化: 1つのTCP接続内で複数のリクエスト/レスポンスを並行して双方向にやり取りできる(ストリーミング)。これにより、接続のオーバーヘッドが劇的に減少する。
- Protocol Buffers (Protobuf):
gRPCの標準データ形式(シリアライズ形式)。JSONやXMLの代わりに使われる。
- 高速なバイナリ形式: テキストではなくコンパクトなバイナリ形式でデータを送受信するため、データ量が小さく、JSONの何倍も高速にシリアライズ・デシリアライズ(解析)できる。
- 厳格なスキーマ定義:
.protoという専用ファイルでAPIの仕様(関数やデータ構造)を厳密に定義する。このファイルから各言語(Go, Java, Python, Node.jsなど)のコードが自動生成されるため、サーバーとクライアント間の型が保証される。
gRPCの主戦場とトレードオフ
この「圧倒的な速度」と「厳格な型定義」から、gRPCの主戦場もまた明確である。
- 主戦場: サーバー間通信(内部バックエンド)。
- マイクロサービス間の通信。
- 低遅延(ローレイテンシ)と高スループット(大量処理)が最優先される内部API。
gRPCもまた、その速度と引き換えにトレードオフを抱えている。
- 人間の可読性の欠如: データがバイナリ形式であるため、
curlコマンドでJSONのように中身を覗いたり、デバッグしたりするのが難しい(専用ツールが必要)。 - ブラウザの非互換性: ブラウザ(JavaScript)は標準でgRPCを直接サポートしていない。ブラウザから利用するには、gRPC-Webというプロキシ(仲介役)を別途立てる必要がある。
- 厳格なスキーマ:
.protoファイルによる厳格な定義は、裏を返せば「ちょっと試してみる」という気軽な開発には向かず、スキーマの変更管理コストが発生する。
3. 徹底比較:REST vs GraphQL vs gRPC
ここで、3つの技術特性を整理し、その違いを明確にする。
| 比較軸 | REST (現実のAPI) | GraphQL | gRPC |
|---|---|---|---|
| 主な用途 | 公開API、シンプルなCRUD | クライアント-サーバー間 | サーバー間 (マイクロサービス) |
| 通信プロトコル | HTTP/1.1 (または2) | HTTP/1.1 (または2) | HTTP/2 (必須) |
| データ形式 | JSON | JSON | Protocol Buffers (バイナリ) |
| 設計思想 | リソース指向 | クエリ指向 (クライアント中心) | サービス指向 (RPC) |
| N+1問題 | 発生しやすい | 根本的に解決する | 発生しない (用途が異なる) |
| オーバーフェッチ | 発生しやすい | 根本的に解決する | 発生しない (スキーマで定義) |
| スキーマ定義 | OpenAPI (任意) | スキーマ定義 (必須) | .proto ファイル (必須) |
| 強み | シンプルさ、普及率、キャッシュ | データ取得の圧倒的柔軟性 | 圧倒的な通信速度、厳格な型 |
| 弱み | N+1問題、オーバーフェッチ | キャッシュが難しい、実装複雑性 | 人間の可読性、ブラウザ非互換 |
考察:競合ではなく「共存」するアーキテクチャ
「GraphQL vs gRPC」という対立構造で語られることが多いが、本記事の比較(特に「主な用途」)を見れば明らかな通り、その構図は多くの場合、誤りである。両者は得意分野が全く異なる。
現代の複雑なWebサービスにおいて、これらは競合するどころか、むしろ「共存」することで最強のアーキテクチャを形成する。
典型的な共存モデルは以下のようになる。
[クライアント (Web/Mobile)] $\downarrow$ (GraphQL) $\leftarrow$ データ取得の柔軟性が最重要 $\downarrow$ [BFF / API Gateway] $\downarrow$ (gRPC) $\leftarrow$ 内部通信の速度が最重要 $\downarrow$ [内部マイクロサービス A] $\leftarrow (gRPC) \rightarrow$ [内部マイクロサービス B]
この構成では、以下の利点を両取りできる。
- 外部(クライアント-BFF間): GraphQL を採用する。
- クライアントは、N+1問題やオーバーフェッチに悩まされることなく、必要なデータだけを1回のリクエストで柔軟に取得できる。
- 内部(BFF-サービス間、サービス-サービス間): gRPC を採用する。
- BFFはクライアントからのGraphQLリクエストを受け取り、それを解釈して、内部の複数のマイクロサービス(例:ユーザーサービス、投稿サービス)をgRPCで高速に呼び出す。
- マイクロサービス間の通信も全てgRPCで行うことで、システム全体の遅延を最小限に抑える。
もちろん、システムがシンプルであったり、広く一般に公開するAPIであったりするならば、普及率とシンプルさ、キャッシュの容易さにおいて、今でもRESTが最適解であるケースは多い。
まとめ
この記事では、RESTの次世代API技術として注目されるGraphQLとgRPCを徹底的に比較した。
- GraphQLは「データ取得の柔軟性」を追求し、クライアント-サーバー間の通信(特にN+1問題やオーバーフェッチ)を劇的に効率化する。主戦場はBFFやモバイルアプリのバックエンドだ。
- gRPCは「通信の絶対速度」を追求し、HTTP/2とバイナリ形式(Protobuf)によってサーバー間の通信を圧倒的に高速化する。主戦場はマイクロサービスの内部通信だ。
最終的に重要なのは、流行の技術に飛びつくことでも、一つの技術に固執することでもない。それぞれの設計思想が「何を解決するために生まれたのか」という本質と、その「トレードオフ」を正確に理解することである。
REST、GraphQL、gRPCは、競合する敵同士ではない。我々の道具箱(ツールボックス)に収められた、それぞれに得意な用途を持つ「異なる道具」に過ぎないのだ。構築すべきシステムの要件に対し、最適な道具を選択する眼を持つことこそが、現代の設計者には求められている。