「データの不整合」という悩み
深夜2時まで、たった一つの undefined エラーを探し続けたことはありませんか?よくあるシナリオです。バックエンドが密かにフィールド名を user_id から userId に変更し、フロントエンドは古い名前を使い続けている。TypeScriptはサーバー側で何が起きたかを知る由もないため、完全にお手上げ状態。結果として、ユーザーの手元でアプリがクラッシュしてしまいます。
根本的な問題は、クライアントとサーバーの間の「溝」にあります。RESTやGraphQLを使用していても、結局は両方のエンドでインターフェースを定義し直す必要があります。これは時間の無駄であり、非常にミスが起きやすい作業です。実際、Swaggerファイルのタイポ一つを直すのに3時間費やしたこともあります。本来、テクノロジーが私たちの代わりに解決すべき問題です。
そこで救世主となるのが tRPC です。これはバックエンドの型をフロントエンドに直接接続し、中間ステップを不要にします。サーバー側のコードを修正した瞬間、クライアント側の VS Code で即座にエラーが表示されます。アプリを実行するまで間違いに気づかない、という状況はもうありません。
tRPCとは何か、なぜ特別なのか?
tRPC(TypeScript Remote Procedure Call)は、決して魔法のようなフレームワークではありません。シンプルでスマートなデータ転送レイヤーです。最大のメリットは、スキーマ定義(.graphql など)が不要で、コード生成も不要である点です。
TypeScriptの型推論(Inference)を最大限に活用します。サーバー側で関数を書けば、クライアント側はその関数がどの引数を必要とし、どのオブジェクトを返すかを自動的に把握します。すべてがリアルタイムで進行します。
プロのヒント:複雑なAPIオブジェクトを扱う際は、toolcraft.app/ja/tools/developer/json-formatter をよく利用します。ロジックに組み込む前に、データの構造を素早くフォーマットして確認できるので、重い拡張機能をいくつも入れるよりずっと便利です。
Next.jsプロジェクトへのtRPCの導入
ここでは TypeScript を使用した Next.js App Router 環境を想定します。新規作成する場合は npx create-next-app@latest を使用してください。次に、tRPCのコアライブラリとデータバリデーション用の Zod をインストールします。
npm install @trpc/client @trpc/server @trpc/react-query @trpc/next @tanstack/react-query zod
Zod は「門番」の役割を果たします。入力データの検証を行うと同時に、tRPCがクライアントから送られてくるデータの構造を理解するのを助けます。
サーバー側(バックエンド)の設定
まず、tRPCの初期化ファイルを作成します。通常、私は src/server/trpc.ts に配置します。
1. tRPCインスタンスの初期化
import { initTRPC } from '@trpc/server';
const t = initTRPC.create();
export const router = t.router;
export const publicProcedure = t.procedure;
2. プロシージャ(APIエンドポイント)の定義
従来の /api/users のようなルートを書く代わりに、ルーター内で定義します。src/server/routers/_app.ts ファイルを作成します。
import { z } from 'zod';
import { router, publicProcedure } from '../trpc';
export const appRouter = router({
getUser: publicProcedure
.input(z.object({ id: z.string() }))
.query(async (opts) => {
const { input } = opts;
return {
id: input.id,
name: '山田 太郎',
email: '[email protected]',
};
}),
});
export type AppRouter = typeof appRouter;
ここで重要なのが export type AppRouter の行です。これこそが「魔法」の正体です。クライアントはこの型(Type)だけをインポートし、ロジック自体は一切インポートしません。これにより、フロントエンドのバンドルサイズを軽量に保つことができます。
3. ルートハンドラーの作成
App Router では、src/app/api/trpc/[trpc]/route.ts でリクエストを受け取るためのハンドラーが必要です。
import { fetchRequestHandler } from '@trpc/server/adapters/fetch';
import { appRouter } from '@/server/routers/_app';
const handler = (req: Request) =>
fetchRequestHandler({
endpoint: '/api/trpc',
req,
router: appRouter,
createContext: () => ({}),
});
export { handler as GET, handler as POST };
クライアント側(フロントエンド)の接続
いよいよ成果を確認する時です。tRPCがどのようにサーバーを呼び出すかを知るためのユーティリティを作成します。
1. React Hooksの作成
src/utils/trpc.ts ファイルを作成します。
import { createTRPCReact } from '@trpc/react-query';
import type { AppRouter } from '@/server/routers/_app';
export const trpc = createTRPCReact<AppRouter>();
2. Providerの設定
tRPC は TanStack Query をベースに動作するため、アプリ全体を Provider でラップする必要があります。src/components/Provider.tsx コンポーネントを作成します。
'use client';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { httpBatchLink } from '@trpc/client';
import React, { useState } from 'react';
import { trpc } from '@/utils/trpc';
export default function Provider({ children }: { children: React.ReactNode }) {
const [queryClient] = useState(() => new QueryClient());
const [trpcClient] = useState(() =>
trpc.createClient({
links: [httpBatchLink({ url: 'http://localhost:3000/api/trpc' })],
})
);
return (
<trpc.Provider client={trpcClient} queryClient={queryClient}>
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
</trpc.Provider>
);
}
コンポーネントでの実際の使用例
ここで tRPC の素晴らしさを実感できるはずです。TypeScript が、サーバー側で記述したプロパティを正確に補完してくれます。
'use client';
import { trpc } from '@/utils/trpc';
export default function UserProfile() {
const userQuery = trpc.getUser.useQuery({ id: '123' });
if (userQuery.isLoading) return <div>読み込み中...</div>;
return (
<div>
<h1>{userQuery.data?.name}</h1>
<p>メールアドレス: {userQuery.data?.email}</p>
</div>
);
}
サーバー側で name を fullName に変更してみてください。すると即座に、クライアント側の userQuery.data?.name の部分に赤い波線のエラーが表示されます。リロードやスクリプトの再実行は不要です。すべてが瞬時に同期されます。
パフォーマンスの最適化とデバッグ
Chrome の Network タブを開くと、/api/trpc/getUser へのリクエストが確認できます。tRPC には Batching(バッチ処理) という非常に優れた機能があります。コンポーネントが同時に3つのAPIを呼び出した場合、tRPC は自動的にそれらを1つのリクエストにまとめます。これにより、ネットワークの遅延を大幅に削減できます。
実体験からのアドバイス:すべてのルートを一つの _app.ts ファイルに詰め込まないでください。プロジェクトが大きくなるにつれ、userRouter や orderRouter のように分割し、t.mergeRouters で統合するようにしましょう。そうすることで、コードが整理され管理しやすくなります。
Zod 用の複雑な正規表現(Regex)をテストする必要がある場合は、toolcraft.app/ja/tools/developer/regex-tester を覗いてみてください。サーバー全体を再起動することなく、正規表現の正確性を素早く検証できます。
おわりに
tRPC は強力ですが、「銀の弾丸」ではありません。モノレポ環境で最も真価を発揮します。サードパーティ向けのパブリックAPIを作成する場合は、REST や GraphQL の方が依然として安全な選択肢です。しかし、TypeScript フルスタックの開発において、tRPC は生産性に革命をもたらします。実行時エラーに悩まされない快適な開発体験を楽しんでください!

