SJ blog
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つのアプローチを比較

RESTGraphQLtRPC
スキーマOpenAPI(任意)必須TypeScript の型が自動的にスキーマになる
型安全性手動 or codegencodegen 必要ネイティブ
オーバーフェッチ起きやすい解決できるフロント側で制御
学習コスト中〜高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