tRPC vs GraphQL 比較

ゼロcodegen、エンドツーエンドのTypeScript型安全性

VS
GraphQL

スキーマファースト、マルチクライアント向けのユニバーサルクエリ言語

16 分で読了Backend

クイック結論

状況に応じて判断すべきであり、「型安全性」という言葉単体はGraphQLを選ぶ理由にはならない。単一のTypeScriptモノレポ+単一のWebクライアントで運用しているなら、tRPCはほぼ常により少ない作業で済む——ゼロcodegenで、サーバー側の変更は即座にクライアント側のTSエラーとして現れる。モバイルアプリがある場合、外部開発者向けに公開するpublic APIがある場合、あるいは複数チームにまたがるフェデレーションが必要な場合は、GraphQLの言語非依存なスキーマ契約とApollo Federationエコシステムが構造的な優位性をもたらす。

tRPCGraphQL
結論をすべて読む

スコア比較

グラフを読み込み中...

詳細スコア

詳細スコア: tRPC GraphQL — カテゴリー別10点満点のスコア
カテゴリーtRPCGraphQL
パフォーマンス
7/10
6/10
学習のしやすさ
8/10
5/10
エコシステム
6/10
9/10
コミュニティ
7/10
8/10
求人市場
5/10
8/10
将来性
7/10
8/10

長所と短所

tRPC

長所

  • コード生成やビルドステップが不要——サーバーの型がそのままクライアントに流れる
  • サーバー側で変更を行うと、ファイルを保存する前にクライアント側でTypeScriptエラーが表示される
  • React、Next.js、Express、Fastify、AWS Lambda、Solid、Svelte向けの公式アダプターがある
  • TanStack Queryとのネイティブ統合——queryKey/queryOptionsのファクトリーがあらかじめ用意されている
  • 公式FAQによると、NetflixやPleoなどの企業が本番環境で使用している
  • TanStack Queryへの依存すらオプション——シンプルなvanillaクライアントだけで十分
  • WebSocketまたはSSEによるsubscriptionsをサポート、SSEの方が設定が簡単として推奨されている
  • MITライセンスで完全にオープンソース——有料のエンタープライズ層は存在しない

短所

  • 型共有は同一のTypeScriptモノレポに依存する——ネイティブモバイルクライアントはこの利点を失う
  • 公式のpublic/REST APIインターフェースがない。`trpc-to-openapi`のようなサードパーティパッケージが必要
  • スキーマのintrospectionがない——外部開発者に対して自己文書化された契約を提供しない
  • フィールド単位の選択(field selection)がない——各プロシージャは固定の戻り値型を持つ
  • バックエンドがJavaScript/TypeScript以外の言語で書かれているシナリオでは使用できない
  • 公式のフェデレーション/複数サービス間のスキーマコンポジション標準が存在しない

最適な用途

単一のTypeScriptモノレポ+単一のWebクライアントで構成されるプロダクトNext.js、Express、またはFastifyベースのフルスタックTSプロジェクト迅速なイテレーションを求める小〜中規模のチーム外部の利用者を持たない社内ツールや管理画面すでにTanStack Queryを使用しているプロジェクト

GraphQL

長所

  • クライアントは必要なフィールドのみを選択する——over/under-fetchingが構造的に解消される
  • 言語非依存のスキーマ契約——iOS、Android、Web、サードパーティなど、あらゆるクライアントにネイティブに対応する
  • introspectionによって自己文書化される。GraphiQLのようなツールで探索可能
  • deprecationの仕組みにより、破壊的変更なしにスキーマを進化させられる
  • 公式の`graphql/dataloader`パッケージにより、N+1問題がバッチ処理とキャッシュで解決される
  • Apollo Federationにより、複数チーム/複数サービスのアーキテクチャで公式なコンポジションサポートがある
  • 2018年以降、Linux Foundation傘下のGraphQL Foundationによって運営されている
  • Relayなどの公式クライアントは、正規化されたキャッシュによって不要な再レンダリングを防ぐ

短所

  • HTTPキャッシュに適したURL構造がない——グローバルID+正規化されたクライアントキャッシュが必要
  • スキーマ設計、リゾルバーアーキテクチャ、dataloaderといった追加の概念が学習曲線を高くする
  • フェデレーションはまだ標準化の途中——Composite Schema Working Groupが現在も活動中
  • エンタープライズ規模ではApollo GraphOSのような層は有料(Developerプラン:100万リクエストあたり5ドル)
  • dataloaderなしでN+1のリスクを放置することは、よく見られるエラーの一種
  • 単純なCRUDのシナリオでは、tRPCやRESTに比べてより多くのボイラープレート(スキーマ+リゾルバー)が必要になる

最適な用途

モバイル(iOS/Android)またはサードパーティの利用者を持つAPI複数チーム/複数サービスの組織でフェデレーションが必要なアーキテクチャ外部開発者に公開するpublic API複雑にネストしたデータグラフをクエリするアプリケーションエンタープライズSLAとスキーマガバナンスが必要な大規模システム

コード比較

tRPC
// tRPC — ルーター定義(サーバー)
import { initTRPC } from '@trpc/server';
import { z } from 'zod';
// db = Prisma/ORM クライアント(別ファイルからインポート)

const t = initTRPC.create();

export const appRouter = t.router({
  getUser: t.procedure
    .input(z.object({ id: z.string() }))
    .query(async ({ input }) => {
      return db.user.findUnique({ where: { id: input.id } });
    }),
});

export type AppRouter = typeof appRouter;

// クライアント — サーバーの型が自動的に推論される、codegenなし
import { createTRPCClient, httpBatchLink } from '@trpc/client';
import type { AppRouter } from './server';

const client = createTRPCClient<AppRouter>({
  links: [httpBatchLink({ url: 'http://localhost:3000/trpc' })],
});

const user = await client.getUser.query({ id: '1' });
// user: { id: string; name: string; ... } — コンパイル時に完全な型安全性
GraphQL
// graphql-js + @apollo/server — スキーマ、リゾルバー、サーバーを1ファイルに
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
// db = Prisma/ORM クライアント(別ファイルからインポート)

const typeDefs = `#graphql
  type Post {
    id: ID!
    title: String!
  }

  type User {
    id: ID!
    name: String!
    posts: [Post!]!
  }

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

const resolvers = {
  Query: {
    user: (_parent, { id }, { db }) => db.user.findUnique({ where: { id } }),
  },
  User: {
    posts: (user, _args, { db }) =>
      db.post.findMany({ where: { authorId: user.id } }),
  },
};

const server = new ApolloServer({ typeDefs, resolvers });

const { url } = await startStandaloneServer(server, {
  context: async () => ({ db }),
  listen: { port: 4000 },
});
console.log(`サーバー起動完了: ${url}`);

// クライアントクエリ — 必要なフィールドのみが選択される:
// query { user(id: "1") { name posts { title } } }

結論

状況に応じて判断すべきであり、「型安全性」という言葉単体はGraphQLを選ぶ理由にはならない。単一のTypeScriptモノレポ+単一のWebクライアントで運用しているなら、tRPCはほぼ常により少ない作業で済む——ゼロcodegenで、サーバー側の変更は即座にクライアント側のTSエラーとして現れる。モバイルアプリがある場合、外部開発者向けに公開するpublic APIがある場合、あるいは複数チームにまたがるフェデレーションが必要な場合は、GraphQLの言語非依存なスキーマ契約とApollo Federationエコシステムが構造的な優位性をもたらす。

無料相談を受ける
FAQ

よくある質問

単一のTypeScriptモノレポで単一のWebクライアントのみを運用しているなら、tRPCの方が少ないコードと設定で済む(ゼロcodegen、エンドツーエンドのTS推論 — trpc.io)。モバイルアプリ、外部開発者向けのpublic API、あるいは複数の独立したサービスがある場合は、GraphQLのスキーマ契約とフェデレーションエコシステム(Apollo Federation)の方が適切な抽象化を提供する。

関連ブログ記事

すべての記事を見る
すべての比較