REST vs GraphQL vs tRPC — API スタイルの選択基準
新しいサービスの API を設計するとき、「REST にするか、GraphQL にするか、最近は tRPC もあるらしい」という議論は必ず一度は起こります。結論を先に書くと、3 つは優劣の関係ではなく、異なる制約条件に最適化された道具であり、決定の軸は技術ではなく「誰がこの API を消費するのか」です。 この記事では、各スタイルが何を解決し何を新たに抱えるのか、そして選択基準を整理します。
REST — リソースと HTTP セマンティクスの上のデフォルト #
REST はデータをリソース(URL)として表現し、HTTP メソッド(GET、POST、PUT、DELETE)で操作するスタイルです。過去 20 年の Web API のデフォルトであり、その地位には理由があります。
- HTTP のインフラを無料で手に入れられます。 GET のキャッシュ(CDN、ブラウザ)、ステータスコード、認証ヘッダー、リダイレクトまで、Web のあらゆる中間装置が REST を理解します。
- 学習・ツールのコストが最低です。 curl ひとつでデバッグでき、どの言語のどのクライアントからも呼べて、OpenAPI 仕様によるドキュメント・クライアント生成のエコシステムが成熟しています。
弱点は、エンドポイントの形がサーバーの決めたとおりに固定されることです。画面ひとつを描くのに必要なデータがサーバーの作ったレスポンスとずれると、2 つの問題が生まれます。不要なフィールドまで受け取るオーバーフェッチ、そしてひとつの画面のために複数のエンドポイントを連続で呼ぶアンダーフェッチです。クライアントの種類が増え画面が複雑になるほどこのずれは大きくなり、「画面専用エンドポイント」が増えて API が散らかっていきます。
GraphQL — クライアントが必要な形を宣言する #
GraphQL はこのずれを正面から解決します。サーバーは型で定義されたスキーマ(データグラフ)をひとつ公開し、クライアントが必要なフィールドだけをクエリで宣言して、1 回のリクエストで受け取ります。オーバーフェッチとアンダーフェッチは定義上消え、スキーマがそのまま契約でありドキュメントになります。モバイル・Web・パートナーなど、異なるデータ要求を持つクライアントが複数あるとき、力が最大化されます。
代わりに新たに抱えるコストも明確です。
- サーバーの複雑さ: リゾルバーの構造上 N+1 問題がデフォルトで発生し、DataLoader のようなバッチング装置が必須になります。またクライアントが任意に深いクエリを作れるため、クエリの複雑度制限・深さ制限といった防御が必要です。
- HTTP キャッシュの喪失: すべてのリクエストが実質 POST ひとつで届くので、CDN・ブラウザのキャッシュをそのまま使えず、キャッシュはクライアントライブラリ(Apollo など)の正規化キャッシュの層へ移ります。
- 運用の学習コスト: スキーマ設計、バージョニングの慣行、監視(どのクエリが遅いか)まで、REST とは別の運用スキルが必要です。
要約すると、GraphQL は柔軟性をクライアントに渡し、複雑さをサーバーが買い取る取引です。その取引が得になるには、クライアント側の多様性が実際に存在しなければなりません。
tRPC — 型を端から端まで、ただし TypeScript の中だけで #
tRPC は問いそのものを変えます。「サーバーとクライアントが同じリポジトリの TypeScript なら、スキーマもコード生成も要らないのでは?」サーバーにプロシージャ(関数)を定義すると、クライアントはその型をコンパイル時にそのまま共有して、自動補完と型チェックの効くリモート呼び出しを手に入れます。
- スキーマファイルも、コード生成のステップもありません。サーバー関数のシグネチャを変えると、クライアントのコードがその場で型エラーとして壊れます。API の不一致がランタイムではなくコンパイル時に捕まる開発体験が、tRPC のすべてであり最大の強みです。
- 適用条件は明確です。フルスタック TypeScript + モノレポ(または型を共有できる構造)+ 内部の消費者。 Next.js のフルスタックアプリのフロント・バック間通信が典型的な適合例です。
限界も条件の裏返しそのままです。TypeScript の外の消費者(モバイルネイティブ、別言語のサーバー、外部パートナー)が現れた瞬間、型共有という前提が崩れます。つまり tRPC は公開 API の道具ではなく、ひとつのチームが所有するアプリの内部通信の道具です。
選択基準 — 3 つの問い #
| 問い | 答え | 推奨 |
|---|---|---|
| 外部に公開される API か? | はい | REST(+ OpenAPI)。消費者を制御できないなら普遍性が勝ちます |
| データ要求の異なるクライアントが複数あるか? | はい | GraphQL。サーバーの複雑さのコストを払う名分が立ちます |
| フルスタック TS 単一チームの内部通信か? | はい | tRPC。スキーマ・コード生成なしの型安全が最適です |
いくつか補足の基準があります。
- 迷ったら REST がデフォルトです。 選択を誤ったときのコストが最も低く、あとで特定の画面構成向けに GraphQL の層(BFF)を載せる進化の道も開いています。
- 併用は正常です。 外部公開は REST、自社クライアント向けは GraphQL または tRPC という構成は妥協ではなく、消費者ごとに合う道具を使った結果です。
- 性能は決定の軸ではありません。 3 つともうまく作れば速く、へたに作れば遅いです。API が遅いときの診断はスタイルと無関係に、ボトルネックを探す順序の問題です。
まとめ #
- REST は HTTP のインフラと普遍性を無料で得るデフォルトで、弱点はサーバーが固定したレスポンスの形から来るオーバー・アンダーフェッチです。
- GraphQL はクライアントに必要なデータを宣言させてそのずれをなくす代わりに、N+1・クエリ防御・キャッシュ再設計というサーバーの複雑さを買い取ります。多様なクライアントがいるときに得になる取引です。
- tRPC はフルスタック TypeScript の内部通信で、スキーマなしのコンパイル時型安全をくれますが、TS 外の消費者が現れた瞬間に前提が崩れます。
- 決定の軸は消費者です。公開 = REST、多様なクライアント = GraphQL、TS 単一チームの内部 = tRPC。迷ったら REST から始めて、必要になったら載せます。