tRPC を使い始めて最初に面食らうのは、1箇所の型を直すと別のファイルで7個のエラーが芋づる式に出てくる瞬間ではないでしょうか。私も個人プロジェクトで tRPC v11 を導入したとき、appRouter の型を少しいじっただけでクライアント側の自動補完が全滅し、エディタが真っ赤になったことがあります。このとき役に立ったのが Claude Code の「関連ファイルをまとめて読み解く力」でした。
ここではtRPC と Claude Code を組み合わせて型エラーを素早く解消し、かつ設計段階から型安全を崩さないための具体的なワークフローをお伝えします。公式ドキュメントの手順を追うだけでは気づきにくい、実運用で詰まるポイントに焦点を当てています。
なぜ tRPC × Claude Code の相性が良いのか
tRPC の真価は「サーバー側のルーター定義を変更した瞬間に、クライアントの型補完も更新される」点にあります。しかしその連鎖性が仇となり、型エラーが発生すると原因箇所を特定するために複数のファイルを往復する必要が出てきます。
Claude Code は複数ファイルを並列で読み込んで依存関係を追えるため、このタイプの問題に強いです。私の体感では、一人で根気よく追うと30分かかる型エラーが、5分程度で解けるようになりました。
特に有効なのは次の3場面です。
- ルーターのリファクタリング: プロシージャを別ルーターに移動したとき、呼び出し元の型参照を一括で書き換える
- 入力スキーマの変更: Zod スキーマを修正したときに、関連するコンポーネントの props 型まで伝播させる
- ミドルウェアの型合成:
createTRPCRouterのコンテキスト型を変えたときに、認証系プロシージャ全体を矛盾なく更新する
プロジェクトの最小構成をセットアップする
まずは動くところまで持っていきます。Next.js 15 の App Router + tRPC v11 を前提とした最小構成は次の通りです。
# 依存関係のインストール
npm install @trpc/server @trpc/client @trpc/react-query @tanstack/react-query zod
npm install -D typescript @types/nodeサーバー側のルーターは src/server/routers/_app.ts に置き、クライアント側のフックを src/lib/trpc.ts に集約する構成が扱いやすいです。
// src/server/routers/_app.ts
import { z } from "zod";
import { initTRPC } from "@trpc/server";
const t = initTRPC.create();
export const appRouter = t.router({
hello: t.procedure
.input(z.object({ name: z.string().min(1) }))
.query(({ input }) => {
return { message: `こんにちは、${input.name}さん` };
}),
});
// 型だけをエクスポート(実装はエクスポートしない)
export type AppRouter = typeof appRouter;ポイントは最後の行です。クライアント側には AppRouter の型情報だけが渡り、実装コード(データベースアクセス等)はバンドルされません。この「型だけ流通させる」設計が tRPC の本質で、ここを理解していると後の型エラー解読が楽になります。
ルーター設計で Claude Code に任せる範囲を決める
ここが重要な判断ポイントです。tRPC のルーター設計を最初から全部 AI に任せると、プロシージャの粒度が揃わずメンテナンス性が落ちます。私は次のように分業しています。
人間が握る部分:
- ルーターの分割方針(
userRouter・postRouterのような境界線) - 認証・認可の判断基準
- エラーハンドリングのポリシー
Claude Code に任せる部分:
- Zod スキーマと TypeScript の型定義の整合
- クライアント側のフック呼び出しコード
- テストケースの雛形生成
具体的な依頼の仕方はこんな感じです。
src/server/routers/user.ts に以下を追加してほしい。
- createUser: 入力 { email: string, name: string }、出力 { id: string }
- getUser: 入力 { id: string }、出力 User または null
バリデーションは Zod を使い、メールアドレスは RFC に準拠した形式のみ許可。
実装は仮で console.log を返すだけでよく、DB アクセスは後で私が書く。
設計は人が決めて、型の受け渡しとバリデーションは AI に任せる、という切り分けが現場で一番回ります。
型エラーを一瞬で解決するデバッグワークフロー
ここが本記事の核心です。tRPC で最も頻発する型エラーは、次の3パターンに集約されます。
パターン1: 「このプロシージャは存在しません」
クライアント側で trpc.user.create.useMutation() を呼んだときに「Property 'create' does not exist」と怒られるケースです。原因は大抵、appRouter に userRouter を登録し忘れているか、エクスポートパスが間違っています。
Claude Code への投げ方:
src/lib/trpc.ts で trpc.user.create が型エラー。
src/server/routers/_app.ts と src/server/routers/user.ts を見て、
user ルーターが正しく登録されているか確認してほしい。
Claude Code は両ファイルを開いて _app.ts の t.router({ ... }) 内の登録漏れを即座に特定してくれます。
パターン2: 「入力の型が合いません」
Zod スキーマを変更したあとにクライアント側の呼び出しが壊れるケースです。ここは全ての呼び出し箇所を検索する必要があります。
src/server/routers/post.ts の createPost の入力スキーマを
{ title: string, body: string } から { title: string, body: string, tags: string[] } に
変更した。全ての呼び出し箇所を検索し、tags を渡すように修正してほしい。
tags はとりあえず空配列 [] を渡せばよい。
Claude Code は grep 相当の検索を行い、該当箇所を全部見つけて修正差分を提案してくれます。
パターン3: 「サーバー型とクライアント型の不一致」
export type AppRouter の型が古いまま、クライアント側のビルドキャッシュが残っているケースです。これは型エラーというより環境問題なので、対処法は次の順番で試してください。
.nextフォルダを削除- TypeScript のビルドキャッシュ(
tsconfig.tsbuildinfo)を削除 - IDE の TypeScript サーバーを再起動
- それでもダメなら
node_modules/.cacheを削除
このトラブルシュートの流れ自体を Claude Code に覚えさせておくと、同じ症状が出たときに即座に実行してくれます。プロジェクトメモリ(CLAUDE.md)に「tRPC の型がクライアント側に反映されないときは以下を順に試す」と書いておくのが実用的です。
私が実際に詰まった落とし穴
ここまでは綺麗な流れを書きましたが、現場ではもっと泥臭い問題にぶつかります。実際に詰まった3つを共有します。
ミドルウェアの型がなぜか any に落ちる: createTRPCRouter のコンテキスト型を推論させようとすると、ミドルウェアの戻り値が any になることがあります。原因は t.middleware の定義で型引数を省略していたこと。明示的に t.middleware<{ userId: string }>(async ({ ctx, next }) => ...) と書くことで解決しました。
Zod の .optional() と TypeScript の ? の食い違い: z.object({ name: z.string().optional() }) は { name?: string | undefined } になります。クライアント側で { name: "" } を渡すと通りますが、サーバー側で空文字を許容するかは別の判断です。型だけで安心せず、ビジネスロジックの検証を入れるべき箇所です。
tRPC + React Query の inferQueryInput が長大になる: プロシージャ入力の型を参照するときに inferRouterInputs<AppRouter>["user"]["getUser"] のような記述になり、コードが読みにくくなります。私は src/lib/trpc-types.ts に型エイリアスを集約し、GetUserInput のような短い名前で参照するようにしています。
次の一歩
tRPC を採用するかどうかで迷っているなら、まずは既存の Next.js プロジェクトの中で1つだけプロシージャを作ってみてください。api/hello エンドポイントを tRPC に置き換えるだけでも、型補完の快適さが体感できます。そこから徐々に広げていくのが、破綻しない導入方法です。
Claude Code と組み合わせて開発する場合、プロジェクトルートの CLAUDE.md に「このプロジェクトは tRPC v11 を使用。ルーターは src/server/routers/ 配下。新規プロシージャを追加するときは Zod スキーマを必ず定義する」と書いておくと、以降の提案品質が目に見えて上がります。
tRPC を採用しない方がいい場面
正直にお伝えすると、tRPC はすべてのプロジェクトに合うわけではありません。私なら以下のような場面では別の選択肢を取ります。
-
外部パートナーに公開する API: tRPC はクライアントとサーバーで TypeScript の型を共有する前提で動きます。外部公開 API であれば OpenAPI や GraphQL を選ぶべきです
-
大容量バイナリを扱うエンドポイント: ファイルアップロード・ストリーミング・バイナリ形式は tRPC の request/response モデルに馴染みません。
multipart/form-dataを扱う普通の REST エンドポイントが素直です -
多言語クライアントを抱えるチーム: モバイルが Kotlin や Swift でフロントが TypeScript という構成では、TypeScript 外に型共有のメリットが及びません
逆に言えば「同じエンジニアが TypeScript のサーバーとクライアントを両方触る」モノレポ体制なら、tRPC の生産性向上は確実に実感できます。
もう一つ身につけたい Claude Code との付き合い方
Claude Code が tRPC コードを提案してきたとき、私は必ず「なぜこの書き方を選んだのか」を聞くようにしています。「ここで inferRouterOutputs を使った理由は?」と一言添えるだけで、モデルの判断根拠が引き出され、後から発覚する細かい型ミスを未然に防げます。この数秒の確認でデバッグセッションが何度も救われました。
tRPC のように高度な型推論を活用するライブラリを読み解く基礎体力がつく一冊です。
また、Claude Code のフックや MCP 連携をより本格的に活用したい方は、Claude Code Hooks 自動化マスターガイドで事前・事後チェックの設定パターンを解説しています。tRPC のルーター変更時に型生成を自動化する流れを作ると、さらに開発速度が上がります。