※本記事には広告(楽天アフィリエイト)リンクが含まれる場合があります。
「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で表現します。UNAUTHORIZED・FORBIDDEN・NOT_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知識だけで導入できます。
- 自前フロントエンド専用ならtRPCが最速 — 開発速度と型安全性の両立は他の方式より優れています。
- 公開API・多言語クライアントならREST/GraphQL — 使い分けが重要です。
- Next.jsでもHono+Workersでも導入可能 — 2026年の主要フレームワークで問題なく動きます。
- React Queryとの連携でキャッシュ・楽観的更新も型安全 — フロントエンドのUXも犠牲になりません。
個人開発のスピードと品質を両立したいなら、tRPCは2026年現在、最有力の選択肢の1つです。まずは小さなAPIから試して、型安全フルスタック開発の快適さを体感してみてください。