backend
A
信頼度ランク
| S | 公式ソース確認済み |
| A | 成功実績多数・失敗例少数 |
| B | 賛否両論 |
| C | 動作未確認・セキュリティリスク高 |
| Z | 個人所感 |
GraphQL vs REST vs tRPC:2026年に選ぶべきAPI設計
フロントエンドとバックエンドのAPI通信をどう設計するか。REST・GraphQL・tRPCのトレードオフを整理し、プロジェクト規模・チーム構成に合わせた選択ガイドを提示します。
一言結論
外部公開APIにはREST、複数クライアントへの柔軟なデータ提供にはGraphQL、TypeScriptフルスタック環境での型安全な内部APIにはtRPCが最適であり、プロジェクト規模とチーム構成で選択すべきだ。
3つのアプローチを比較
| REST | GraphQL | tRPC | |
|---|---|---|---|
| スキーマ | OpenAPI(任意) | 必須 | TypeScript の型が自動的にスキーマになる |
| 型安全性 | 手動 or codegen | codegen 必要 | ネイティブ |
| オーバーフェッチ | 起きやすい | 解決できる | フロント側で制御 |
| 学習コスト | 低 | 中〜高 | TypeScript 必須 |
| 採用事例 | 最多 | GitHub・Twitter | フルスタック TS プロジェクト |
REST — シンプルで普遍的
向いているプロジェクト: 外部公開 API・モバイル・マイクロサービス間通信
GET /users 一覧
POST /users 作成
GET /users/:id 取得
PUT /users/:id 更新
DELETE /users/:id 削除
// Express + OpenAPI の例
app.get("/users/:id", async (req, res) => {
const user = await db.users.findById(req.params.id);
if (!user) return res.status(404).json({ error: "Not Found" });
res.json(user);
});
問題点: 画面が必要とするデータ構造がエンドポイントと一致しないと、オーバーフェッチやアンダーフェッチが起きる。
GraphQL — 柔軟なデータ取得
向いているプロジェクト: 複数クライアント(Web・iOS・Android)、データ要件が多様
# クライアントが必要なフィールドだけ指定できる
query {
user(id: "1") {
name
posts(limit: 5) {
title
createdAt
}
}
}
// Pothos(TypeScript ファースト GraphQL スキーマビルダー)
const UserType = builder.objectType("User", {
fields: (t) => ({
id: t.exposeID("id"),
name: t.exposeString("name"),
posts: t.field({
type: [PostType],
resolve: (user) => db.posts.findByUserId(user.id),
}),
}),
});
問題点: N+1 問題が起きやすい(DataLoader で解決)、キャッシュが複雑、スキーマ管理コスト。
tRPC — フルスタック TypeScript の新定番
向いているプロジェクト: Next.js・SvelteKit などのフルスタック TypeScript プロジェクト
サーバー側で定義した型が、クライアント側にそのまま伝わります。コード生成不要。
// server/router.ts
import { initTRPC } from "@trpc/server";
import { z } from "zod";
const t = initTRPC.create();
export const appRouter = t.router({
users: t.router({
list: t.procedure.query(async () => {
return await db.users.findMany();
}),
getById: t.procedure
.input(z.object({ id: z.string() }))
.query(async ({ input }) => {
return await db.users.findById(input.id);
}),
create: t.procedure
.input(z.object({ name: z.string(), email: z.string().email() }))
.mutation(async ({ input }) => {
return await db.users.create(input);
}),
}),
});
export type AppRouter = typeof appRouter;
// client/pages/users.tsx
import { trpc } from "../utils/trpc";
function UsersPage() {
const users = trpc.users.list.useQuery(); // 型が自動で推論される
return (
<ul>
{users.data?.map((u) => <li key={u.id}>{u.name}</li>)}
</ul>
);
}
型エラーが即座に IDE に出るため、バックエンドの変更が フロントエンドに瞬時に反映されます。
選択ガイド
外部公開 API(他社が使う) → REST + OpenAPI
複数クライアント(iOS含む) → GraphQL
フルスタック TypeScript → tRPC
既存 REST API へのアダプター → REST のまま or BFF で tRPC/GraphQL
まとめ
- REST: 最も汎用的。外部公開や多言語クライアントがある場合は必須
- GraphQL: データ要件が複雑で複数クライアントがある場合に強い
- tRPC: TypeScript フルスタックなら最高の DX。ただし TS 限定
2026年時点で最も勢いがあるのは tRPC で、Next.js などのフルスタックフレームワークとの組み合わせで急速に普及しています。
参考: tRPC 公式 / GraphQL 公式 / OpenAPI