※本記事には広告(楽天アフィリエイト)リンクが含まれる場合があります。

「APIの型定義がズレて、フロントエンドで実行時エラーが出た……」

TypeScriptでフルスタック開発をしていると、フロントエンドとバックエンドの型のズレが頻繁に起こります。APIレスポンスの型を手書きで管理し、変更のたびに両方直す——そんな非効率な開発を続けていませんか?

tRPCは、TypeScriptの型をサーバーとクライアントで完全に共有できるRPCライブラリです。スキーマ定義やコード生成が不要で、型推論だけでエンドツーエンドの型安全性を実現します。Next.js・Hono・Expressなど、主要なフレームワークと組み合わせて使えるのが特徴です。

本記事では、tRPC v11をベースに、基本概念から実践的なセットアップ、認証・エラーハンドリング・React Query連携までを、実際のコード例付きで完全解説します。

広告・楽天アフィリエイト

メカニカルキーボード

打鍵感で選ぶ開発者向けキーボード。

楽天ポイント還元

キーボードを楽天で見る →

1. tRPCとは何か?REST・GraphQLとの違い

tRPCは「TypeScript Remote Procedure Call」の略で、TypeScriptの関数をそのままAPIとして公開する仕組みです。サーバー側で定義した関数(プロシージャ)を、クライアントから型安全に呼び出せます。

1.1 なぜtRPCが選ばれるのか

  • スキーマ定義が不要 — GraphQLのようにスキーマ言語(SDL)を書く必要がない。TypeScriptの型がそのまま契約になる。
  • コード生成が不要 — OpenAPIのようにクライアントコードを生成する手間がない。
  • 完全な型推論 — サーバーの型がそのままクライアントに伝播し、補完・エラー検出が完璧。
  • 軽量 — 依存が少なく、バンドルサイズも小さい。

1.2 3大API方式の比較

項目 tRPC REST GraphQL
型安全性 ✅ エンドツーエンドで完璧 ⚠️ 手動で型を合わせる必要 ✅ コード生成で可能
スキーマ定義 不要(TS型が契約) OpenAPIなど任意 SDL必須
学習コスト 低い(TSの知識だけでOK) 低い 中〜高
エコシステム TypeScript限定 全言語対応 全言語対応
キャッシュ・CDN ⚠️ HTTPキャッシュは工夫が必要 ✅ 標準的に可能 ⚠️ エンドポイント共通のため工夫が必要
最適な用途 TSフルスタック(Next.js/Honoなど) 公開API・外部連携 複雑なデータ取得・複数クライアント

ポイントは「誰がAPIを消費するか」です。自前のフロントエンドだけで使うならtRPCが最強ですが、外部に公開するAPIや他言語のクライアントが使うならREST/GraphQLを選ぶのが無難です。

2. セットアップ:Next.js App Router + tRPC v11

ここからは、実際にコードを書いていきます。2026年現在の最新メジャーはtRPC v11です。Next.js App Routerとの組み合わせが最も一般的なので、その構成で解説します。

2.1 パッケージのインストール

npm install @trpc/server @trpc/client @trpc/react-query @trpc/next
npm install @tanstack/react-query zod superjson

zodは入力バリデーション、superjsonはDate型などの特殊な値をシリアライズするために使います。tRPCの公式推奨スタックです。

2.2 サーバー側の初期化(trpc/init.ts)

import { initTRPC } from '@trpc/server';
import superjson from 'superjson';
import { ZodError } from 'zod';

export const t = initTRPC.create({
  transformer: superjson,
  errorFormatter({ shape, error }) {
    return {
      ...shape,
      data: {
        ...shape.data,
        zodError: error.cause instanceof ZodError ? error.cause.flatten() : null,
      },
    };
  },
});

ここで作成した t を使って、ルーターやプロシージャ(API関数)を定義していきます。errorFormatterでZodのバリデーションエラーをクライアントに型付きで渡せるのがポイントです。

2.3 ルーターの定義(server/trpc.ts)

import { z } from 'zod';
import { t } from './init';

// 公開プロシージャ(認証不要)
export const publicProcedure = t.procedure;

// 認証済みプロシージャ(後述のミドルウェアで実装)
export const protectedProcedure = t.procedure.use(/* auth middleware */);

export const appRouter = t.router({
  hello: publicProcedure
    .input(z.object({ name: z.string().min(1) }))
    .query(({ input }) => {
      return { message: `Hello, ${input.name}!` };
    }),
  getPosts: publicProcedure
    .query(async () => {
      return await db.select().from(posts);
    }),
  createPost: protectedProcedure
    .input(z.object({
      title: z.string().min(1).max(100),
      body: z.string().min(1),
    }))
    .mutation(async ({ input, ctx }) => {
      return await db.insert(posts).values({
        ...input,
        authorId: ctx.user.id,
      }).returning();
    }),
});

export type AppRouter = typeof appRouter;

queryはデータ取得(GET相当)、mutationはデータ変更(POST/PUT/DELETE相当)です。inputにはZodスキーマを渡すだけで、バリデーションと型推論が同時に効きます

広告・楽天アフィリエイト

プログラミング技術書

実践寄りの技術書・参考書を探す。

楽天ポイント還元

技術書を楽天で見る →

3. クライアント側のセットアップ

3.1 APIクライアントの作成(trpc/client.ts)

import { createTRPCReact } from '@trpc/react-query';
import type { AppRouter } from '@/server/trpc';

export const trpc = createTRPCReact<AppRouter>();

たったこれだけで、AppRouterの型がクライアント全体に伝播します。trpc.hello.useQuery() のように、フックとして型安全に呼び出せます。サーバーの関数名や引数が変われば、クライアント側でも即座に型エラーとして検出されます。

3.2 Providerの設定

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { httpBatchLink } from '@trpc/client';
import { useState } from 'react';
import { trpc } from './trpc/client';

export function TRPCProvider({ children }: { children: React.ReactNode }) {
  const [queryClient] = useState(() => new QueryClient());
  const [trpcClient] = useState(() =>
    trpc.createClient({
      links: [
        httpBatchLink({
          url: '/api/trpc',
        }),
      ],
    })
  );

  return (
    <trpc.Provider client={trpcClient} queryClient={queryClient}>
      <QueryClientProvider client={queryClient}>
        {children}
      </QueryClientProvider>
    </trpc.Provider>
  );
}

httpBatchLinkは、同時に発生した複数のリクエストを1つにまとめるバッチ処理を自動で行います。リクエスト数が減るので、ネットワーク効率が良くなります。

4. 認証ミドルウェアの実装

本番アプリでは認証が必須です。tRPCではミドルウェアで認証を一元管理できます。

import { TRPCError } from '@trpc/server';
import { t } from './init';

export const protectedProcedure = t.procedure.use(async ({ ctx, next }) => {
  // ctxにはリクエストから取得したセッション情報が入る
  if (!ctx.user) {
    throw new TRPCError({ code: 'UNAUTHORIZED' });
  }
  return next({
    ctx: {
      user: ctx.user,
    },
  });
});

認証エラーはTRPCErrorで表現します。UNAUTHORIZEDFORBIDDENNOT_FOUNDなど、HTTPステータスに対応したコードが用意されています。

セッション管理には、NextAuth(Auth.js)やBetter Authを組み合わせるのが一般的です。Auth.jsで取得したセッションを createContext で tRPC の ctx に渡します。

5. React Queryとの連携:データ取得のベストプラクティス

tRPCの最大の強みは、React Query(TanStack Query)とシームレスに連携できることです。キャッシュ・再取得・楽観的更新がすべて型安全に使えます。

5.1 データ取得(クエリ)

function PostList() {
  const { data, isLoading, error } = trpc.getPosts.useQuery();

  if (isLoading) return <p>読み込み中...</p>;
  if (error) return <p>エラー: {error.message}</p>;

  return (
    <ul>
      {data?.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}

5.2 楽観的更新(ミューテーション)

function CreatePost() {
  const utils = trpc.useUtils();

  const createPost = trpc.createPost.useMutation({
    onSuccess: () => {
      // 投稿一覧のキャッシュを自動更新
      utils.getPosts.invalidate();
    },
  });

  return (
    <form
      onSubmit={(e) => {
        e.preventDefault();
        const form = new FormData(e.currentTarget);
        createPost.mutate({
          title: String(form.get('title')),
          body: String(form.get('body')),
        });
      }}
    >
      <input name="title" placeholder="タイトル" />
      <textarea name="body" placeholder="本文" />
      <button type="submit">投稿する</button>
    </form>
  );
}

useUtils().getPosts.invalidate() で、投稿一覧のキャッシュを無効化して自動再取得します。「投稿後に一覧が更新されない」問題を、型安全なまま解決できます。

6. Hono + tRPCでサーバーレスAPIを構築する

Next.js以外の選択肢として、Hono + tRPCでCloudflare Workersにデプロイする構成も人気です。RAGガイドやCloudflare Workersの記事で紹介した構成とも相性が良いです。

import { Hono } from 'hono';
import { trpcServer } from '@hono/trpc-server';
import { appRouter } from './trpc';

const app = new Hono();

app.use(
  '/trpc/*',
  trpcServer({
    router: appRouter,
  })
);

export default app;

HonoのミドルウェアとしてtRPCサーバーをマウントするだけで、Cloudflare Workers上で型安全なAPIを公開できます。エッジでの実行なので、レイテンシも低く抑えられます。

7. tRPCを採用するときの注意点と回避策

7.1 デメリット・注意点

  • TypeScript必須 — 他言語のクライアントからは使えない。公開APIには不向き。
  • HTTPキャッシュが効きにくい — エンドポイントが1つに集約されるため。GETクエリは httpLink を使う・キャッシュヘッダーを設定するなど対策が必要。
  • スキーマがない — API契約書としての役割を果たさない。外部チームとの連携にはOpenAPIの併用を検討。
  • バンドルサイズ — サーバーの型がクライアントに渡るため、過度に大きな型定義はビルドに影響しうる。

7.2 こんな構成なら最適

ケース 推奨構成
Next.js + 自前フロントのみ tRPC + React Query — 最速で型安全
外部にAPI公開 REST + OpenAPI(tRPCの併用は避ける)
モバイルアプリとWebの両方 REST or GraphQL(tRPCはTSクライアント限定のため)
Cloudflare Workers + Hono Hono + tRPC — エッジで型安全
AI機能をAPIに追加 tRPC + Server Actions併用 or tRPC + OpenAPI変換

8. まとめ:2026年のフルスタック開発は「型安全」が当たり前

tRPCは、「TypeScriptで書くなら、型のズレによるバグをゼロにする」というシンプルな思想のライブラリです。スキーマ定義もコード生成も不要で、既存のTypeScript知識だけで導入できます。

  1. 自前フロントエンド専用ならtRPCが最速 — 開発速度と型安全性の両立は他の方式より優れています。
  2. 公開API・多言語クライアントならREST/GraphQL — 使い分けが重要です。
  3. Next.jsでもHono+Workersでも導入可能 — 2026年の主要フレームワークで問題なく動きます。
  4. React Queryとの連携でキャッシュ・楽観的更新も型安全 — フロントエンドのUXも犠牲になりません。

個人開発のスピードと品質を両立したいなら、tRPCは2026年現在、最有力の選択肢の1つです。まずは小さなAPIから試して、型安全フルスタック開発の快適さを体感してみてください。