← Blog
·11 blog.minutes

API設計:REST vs GraphQL vs gRPC — どう選ぶか

REST、GraphQL、gRPCはそれぞれ異なる問題を解決する。このガイドでは、それぞれの設計哲学、トレードオフ、理想的なユースケースを比較し、次のAPIについて情報に基づいた決定ができるようにする。

現代のあらゆるアプリケーションはAPIを通じて他のアプリケーションと通信する。もはやAPIを構築するかどうかが問題ではなく、どのスタイルのAPIを構築するかが問題である。RESTは10年以上にわたってデフォルトであり続けてきた。GraphQLはRESTの柔軟性のなさへの応答として登場した。gRPCはHTTP/2とプロトコルバッファーに基づいたまったく異なるアプローチを取る。これらの間での選択には、構文だけでなく、それぞれが表す根本的な設計哲学を理解する必要がある。

このガイドでは、各プロトコルを設計の観点から分解する:リソースやスキーマの構造化方法、クライアントとのAPIの相互作用方法、バージョン管理とページネーションの処理方法、リアルタイム要件が決定に与える影響。読み終える頃には、特定の制約に適したツールを選択するためのフレームワークが身につく。

REST:Webが理解するリソース指向設計

REST——Representational State Transfer——はプロトコルではない。ロイ・フィールディングが2000年の博士論文で定義したアーキテクチャスタイルである。中核となる考え方は、ドメインをリソースとしてモデル化し、それぞれをURLで識別し、GET、POST、PUT、PATCH、DELETEという統一されたHTTP動詞のセットを通じてそれらのリソースを操作するというものである。

優れたREST API設計はリソース指向設計である。アクションを中心にエンドポイントを設計するのではない——名詞を中心に設計する。/createUserや/getUserByIdではなく、POST /usersやGET /users/:idを設計する。これは小さな違いに思えるが、APIのスケーリング方法、文書化方法、クライアントの学習方法に深く影響する。

適切に設計されたユーザーリソースのRESTエンドポイントは次のようになる:

GET /users/42
Accept: application/json

Response 200:
{
  "id": 42,
  "name": "Ada Lovelace",
  "email": "ada@example.com",
  "role": "admin",
  "createdAt": "2025-11-14T09:00:00Z"
}

RESTの美しさはそのシンプルさにある。URLがリソースを識別し、HTTPメソッドが操作を識別し、レスポンスボディが表現を含む。あらゆる言語のすべてのHTTPクライアントがこのコントラクトをすでに理解している。HTTPヘッダーを通じてキャッシングがそのまま機能する。OpenAPI(旧Swagger)のようなツールを使用すると、単一のスキーマファイルからドキュメント、クライアントSDK、サーバースタブを生成できる。

RESTは、クライアントが異なる形状のデータを必要とする場合に問題に直面する。モバイルクライアントはユーザーの名前とロールだけを必要とするかもしれないが、ダッシュボードクライアントはネストされた組織データを含む完全なプロファイルを必要とする。RESTでは、複数のエンドポイントを構築するか、すべてのクライアントに完全な表現をダウンロードさせ、不要な部分を破棄させることになる。これが過剰フェッチ問題であり、GraphQLの主な動機であった。

GraphQL:クライアントが必要なものを正確に記述させる

Metaによって開発され2015年にリリースされたGraphQLは、逆のアプローチを取る。サーバーが固定レスポンスを定義する代わりに、クライアントが必要とするデータを正確に記述するクエリを送信し、サーバーがその形状を正確に返す。1つのエンドポイントで、任意のレスポンス形状である。

GraphQLスキーマはドメイン内の型と関係を定義する。クライアントはこのスキーマに対してクエリを構成し、必要なフィールドのみを要求する。RESTのネストされたリソースという同じ概念は、GraphQLクエリではネストされたフィールドになるが、クライアントが深さと選択を制御する。

// GraphQL schema (SDL)
type User {
  id: ID!
  name: String!
  email: String!
  role: Role!
  posts: [Post!]!
}

type Post {
  id: ID!
  title: String!
  body: String!
}

enum Role { USER ADMIN MODERATOR }

type Query {
  user(id: ID!): User
}

ユーザーの名前と投稿タイトルだけを必要とするクライアントは、それらのフィールドを正確に要求するクエリを送信する:

// Client query
query {
  user(id: 42) {
    name
    posts {
      title
    }
  }
}

// Response
{
  "data": {
    "user": {
      "name": "Ada Lovelace",
      "posts": [
        { "title": "Notes on the Analytical Engine" }
      ]
    }
  }
}

GraphQLの重要な設計原則は、スキーマがコントラクトであるということである。スキーマは何が可能かを定義し、クライアントは利用可能なオプションから何を要求するかを決定する。これにより過剰フェッチが排除され、ネットワークリクエストの数が削減され(単一のクエリで複数のRESTラウンドトリップを置き換えられる)、新しいクライアント要件が常に新しいエンドポイントを必要としないため、フロントエンドチームがバックエンドの変更から独立できるようになる。

GraphQLは独自の複雑さも導入する。クエリパフォーマンスは、クライアントが何をロードするかを制御するため、予測が難しくなる。ネストされたフィールドの解決が各親レコードに対して独立したデータベースクエリをトリガーするN+1問題には、DataLoaderのようなバッチングソリューションが必要である。すべてのクエリが同じPOSTエンドポイントにヒットするため、HTTPレベルでのキャッシングは機能せず、アプリケーションレベルのキャッシュ戦略が必要になる。そして、リゾルバー、ミューテーション、サブスクリプション、入力型の学習曲線は、RESTの率直なURLと動詞のモデルよりも急である。

GraphQLはフロントエンドチームにバックエンド変更からの独立性を与える。しかし、その独立性には、クライアントがサーバーに送信するすべてのクエリのパフォーマンス特性を理解する責任が伴う。

gRPC:HTTP/2とprotobuf上に構築された高性能RPC

元Google開発のgRPCは、さらに別のアプローチを取る。リソースやクエリの代わりに、gRPCはAPIをリモートプロシージャコールとしてモデル化する——メソッドを持つサービスを定義し、クライアントはそれらのメソッドをローカル関数であるかのように呼び出す。主要な技術的違いは、gRPCがJSONではなくProtocol Buffers(protobuf)をシリアライゼーションに使用し、HTTP/1.1ではなくHTTP/2をトランスポートに使用することである。

protobufサービスの定義は、RESTエンドポイントやGraphQLスキーマとはまったく異なって見える:

syntax = "proto3";

service UserService {
  rpc GetUser (GetUserRequest) returns (User);
  rpc ListUsers (ListUsersRequest) returns (ListUsersResponse);
  rpc CreateUser (CreateUserRequest) returns (User);
  rpc StreamUserUpdates (StreamRequest) returns (stream UserUpdate);
}

message GetUserRequest {
  int32 id = 1;
}

message User {
  int32 id = 1;
  string name = 2;
  string email = 3;
  Role role = 4;
  string created_at = 5;
}

enum Role { ADMIN = 0; USER = 1; }

message StreamRequest {}

message UserUpdate {
  User user = 1;
  string event_type = 2;
}

ProtobufはJSONよりも大幅に小さく、解析も高速なコンパクトなバイナリ形式にシリアライズする。HTTP/2のマルチプレクシング——単一の接続での複数のストリーム——と組み合わせることで、gRPCは大量通信においてRESTやGraphQLよりも劇的に低いレイテンシと高いスループットを達成する。これによりgRPCは、シリアライゼーションオーバーヘッドの1ミリ秒とワイヤ上の1バイトが重要となるマイクロサービス間通信の支配的な選択肢となっている。

gRPCはまた、4種類のストリーミングをネイティブにサポートしている:ユナリ(1リクエスト、1レスポンス)、サーバーストリーミング(1リクエスト、レスポンスのストリーム)、クライアントストリーミング(リクエストのストリーム、1レスポンス)、双方向ストリーミング(両側が同時にストリーム)。これにより、3つのプロトコルの中でリアルタイムAPIに最適な選択肢となっている。

トレードオフは、gRPCがブラウザから使用するのが難しいことである。ブラウザクライアントはHTTP/2フレームを細かく制御できないため、gRPCサービスに直接アクセスできない。gRPC-Webは回避策として存在するが、複雑さを追加する。Protobufメッセージは転送中に人間が読める形式ではなく、デバッグが難しくなる——トラフィックを検査するにはgrpcurlやリフレクションサービスなどのツールが必要である。また、APIドキュメントのエコシステムはOpenAPIやGraphQLのイントロスペクションシステムほど成熟していない。

実用的な観点からの比較

REST、GraphQL、gRPCの選択が純粋に技術的な決定であることはほとんどない。正しい選択は、クライアントが誰か、どのようなデータが必要か、ネットワークとインフラの制約に依存する。以下は、本番で最も重要な実用的な関心事に関する各プロトコルの比較である。

APIバージョン管理

RESTはURLまたはAcceptヘッダーを通じてバージョン管理を処理する。/v1/usersと/v2/usersは無期限に共存でき、クライアントは自分のペースで移行できる。これはシンプルで実績のある方法だが、重複を促進する——v2エンドポイントは多くの場合、いくつかの変更を加えただけでv1のロジックのほとんどを複製する。GraphQLはスキーマを進化させることでバージョン管理を完全に回避する。非推奨のフィールドには@deprecatedディレクティブが付けられ、すべてのクライアントが移行するまでスキーマに残る。これはすべてのクライアントを制御している場合にはうまく機能するが、クライアントの更新が遅い公開APIでは摩擦を引き起こす可能性がある。gRPCのprotobufスキーマは設計上、前方互換性がある——既存のクライアントを壊さずにフィールドを追加でき、非推奨のフィールドは移行期間後に削除される。これは3つの中で最もエレガントなバージョン管理戦略だが、非推奨サイクルなしにフィールドの名前変更や削除を行わないという規律が必要である。

ページネーションパターン

RESTのページネーションは通常、nextCursorフィールドを使用したカーソルベースのページネーション、またはpageとlimitパラメータを使用したオフセットベースのページネーションで行われる。設計原則は、レスポンスにnextリンクまたはトークンが含まれ、クライアントがそれに従うというものである。GraphQLのページネーションはRelay Connectionスペックを使用し、リストをカーソルとページ情報を持つエッジでラップする——より冗長だが、アドホックなRESTアプローチよりも一貫性がある。gRPCのページネーションはリクエストとレスポンスメッセージ内で手動で処理され、通常はpage_tokenとpage_sizeフィールドを使用する。標準はないが、protobufスキーマがコントラクトを明示的にする。

  • REST:本番APIにはレスポンス内のnextCursorを使用したカーソルベースのページネーションが推奨パターン
  • GraphQL:Relay ConnectionスペックはhasNextPage、hasPreviousPage、カーソルによる標準化されたページネーションモデルを提供
  • gRPC:ページネーションはprotoメッセージ内で定義され、リクエストにpage_tokenとpage_size、レスポンスにnext_page_tokenを含めるのが一般的なパターン

認証と認可

3つのプロトコルすべてが同じ基盤となるトランスポートセキュリティに依存している。RESTは通常、AuthorizationヘッダーにBearerトークンを使用する。GraphQLも同じパターンに従う——すべてのリクエストが単一のPOSTエンドポイントを通るため、トークンはGraphQLレイヤーまたはミドルウェアで検証される。gRPCはインターセプターを使用して各呼び出しに認証メタデータをアタッチし、通常はgRPCメタデータヘッダーで運ばれるJWTトークンの形式を取る。3つのプロトコルはいずれも特定の認証方法を規定していない——OAuth2、APIキー、セッションベース認証の選択はAPIスタイルとは直交する。

レート制限

レート制限は、各エンドポイントが有界な操作を表すため、RESTが最もシンプルである。エンドポイントとクライアントごとにリクエストをカウントし、制限を超えた場合に429 Too Many Requestsを返す。GraphQLでは、単一のクエリが大幅に異なる量の作業をトリガーする可能性があるため、レート制限が難しくなる——1つのフィールドのクエリは1回のデータベースルックアップで済むが、深くネストされたリレーションを持つクエリは50回かかる可能性がある。GraphQL APIは通常、各フィールドに重みがあり、実行前に総クエリコストが計算されるコストベースのレート制限を実装する。gRPCのレート制限はRESTと似ているが、メソッドレベルで動作する——メソッドとクライアントごとにRPC呼び出しをカウントし、ストリーミング呼び出しは開始時だけでなく持続時間にわたってリソースを消費するという追加の考慮事項がある。

リアルタイムAPIとストリーミング

RESTはWebSocketまたはServer-Sent Events(SSE)を通じてリアルタイム更新を処理できるが、どちらもREST仕様の一部ではない——HTTP APIに併設された別個のプロトコルである。GraphQLにはサブスクリプションがあり、これは仕様の第一級の構成要素である。クライアントはイベントを購読し、イベントが発生するたびにWebSocket接続を通じて更新を受信する。gRPCはHTTP/2上のネイティブ双方向ストリーミングにより、最も強力なリアルタイム機能を備えている。単一のgRPC呼び出しで両方向に同時にデータをストリーミングでき、リアルタイムダッシュボード、チャットアプリケーション、イベント駆動型システムに最適である。リアルタイム通信が主要な要件である場合、gRPCのストリーミングが最も成熟したパフォーマンスの高いオプションであり、次いでGraphQLサブスクリプション、REST+WebSocketとなる。

ドキュメントと開発者体験

RESTはOpenAPI(旧Swagger)のおかげで最も強力なドキュメントエコシステムを持つ。OpenAPI仕様は、すべてのエンドポイント、リクエストパラメータ、レスポンススキーマ、認証方法を機械可読なYAMLまたはJSONファイルで記述する。Swagger UIのようなツールはこれをインタラクティブなドキュメントページにレンダリングし、コードジェネレーターは数十の言語向けのクライアントSDKを生成する。このエコシステムの成熟度により、新しい開発者はOpenAPIスペックを読んでから最初のAPI呼び出しを成功させるまでを数分で行える。

GraphQLにはイントロスペクション——特別なエンドポイントを通じてAPIのスキーマをクエリする組み込みシステム——がある。GraphiQLやApollo Studio Explorerなどのツールを使用すると、開発者はスキーマを閲覧し、オートコンプリートでクエリを書き、インラインドキュメントを確認できる。これはすでにAPIの存在を知っている開発者には優れた体験だが、検索エンジンでの発見可能性やOpenAPIスペックのような外部APIマーケットプレイスには役立たない。

gRPCはprotoファイルを信頼できる唯一の情報源として使用する。protoファイルはスキーマでありドキュメントでもある。protoc-gen-docのようなツールはprotoファイルからリファレンスドキュメントを生成し、gRPCリフレクションはクライアントが動的にサービスを発見できるようにする。gRPCの開発者体験は改善されつつあるが、特にマイクロサービスエコシステム以外では、ツールの成熟度においてRESTやGraphQLに依然として劣っている。

それぞれを使用するタイミング

RESTは、APIが外部の開発者によって使用される場合、最大の相互運用性が必要な場合、クライアントが標準のHTTPキャッシングを必要とする場合に適切な選択である。すべての言語、フレームワーク、プロキシがHTTPを理解しているため、最も安全なデフォルトである。公開APIを構築しているなら、RESTから始める。

GraphQLは、APIに異なるデータ要件を持つ複数のクライアントタイプがある場合、フロントエンドチームがバックエンド変更から独立して動く必要がある場合、ネットワークリクエストの数を減らしたい場合に適切な選択である。同じデータの異なる形状を必要とするモバイルとWebクライアントを持つコンシューマー向けアプリケーションで優れている。

gRPCは内部マイクロサービス通信、シリアライゼーションオーバーヘッドが重要な高スループットシステム、リアルタイムストリーミングアプリケーションに適切な選択である。両端を制御下に置き、パフォーマンスが主要な関心事であるシステムを構築している場合、gRPCが最も強力なオプションである。

これらのプロトコルは相互に排他的ではない。一般的な本番アーキテクチャでは、ファイアウォールの背後でgRPCをサービス間通信に使用し、複数のgRPCサービスからのデータを集約するGraphQLゲートウェイを公開し、外部クライアントにはREST APIを提供する。問題は、どこでも1つのプロトコルを使用することではなく、システム内の各インターフェースにどのプロトコルを使用するかである。各プロトコルの背後にある設計哲学を理解することで、その選択を正しく行う判断力が身につく。