tRPCとは?
TypeScript Remote Procedure Callの略
tRPCを利用することで、TypeScriptで定義した型をフロントエンドとバックエンドの双方で共有し、API通信を一貫した型安全性のもとに行える。結果として、IDEの自動補完や型推論のサポートをフルに活用しつつ、クライアントとサーバー間のデータ構造の不一致やバグを未然に防ぎ、効率的な開発を実現できる。
実際に使ってみる
今回はNext.js, trpc, zodを利用してモノレポ構成で利用する。
必要なライブラリのインストール
npm install @trpc/server@next @trpc/client@next @trpc/react-query@next zod
サーバーサイドの実装
trpcのルーターを定義する
trpc/router.ts
import { initTRPC } from '@trpc/server';
import { z } from 'zod';
const t = initTRPC.create();
export const appRouter = t.router({
greeting: t.procedure
.input(z.object({ name: z.string() }))
.query(({ input }) => {
return { message: `Hello, ${input.name}!` };
}),
});
// クライアント用の型
export type AppRouter = typeof appRouter;
trpcエンドポイントの定義
app/api/trpc/[trpc]/route.ts
import { NextRequest } from 'next/server';
import { fetchRequestHandler } from '@trpc/server/adapters/fetch';
import { appRouter } from '@/trpc/router';
export const runtime = 'edge'; // Edge Runtime で動かす場合 (任意)
const handler = (req: NextRequest) => {
return fetchRequestHandler({
router: appRouter,
req,
endpoint: '/api/trpc',
createContext: () => ({}),
});
};
export { handler as GET, handler as POST };
クライアントサイドの実装
trpcクライアントの作成
utils/trpcClient.ts
import { createTRPCProxyClient, httpBatchLink } from '@trpc/client';
import type { AppRouter } from '@/trpc/router';
export const trpcClient = createTRPCProxyClient<AppRouter>({
links: [
httpBatchLink({
url: '/api/trpc', // tRPC のエンドポイント
}),
],
});
画面の作成
app/page.tsx
'use client';
import React, { useState } from 'react';
import { trpcClient } from '@/utils/trpcClient';
export default function Home() {
const [name, setName] = useState('Alice');
const [result, setResult] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
const handleClick = async () => {
setLoading(true);
try {
// tRPC の greeting メソッドを呼び出し
const response = await trpcClient.greeting.query({ name });
setResult(response.message);
} catch (error) {
console.error(error);
setResult('エラーが発生しました');
} finally {
setLoading(false);
}
};
return (
<main style={{ padding: '1rem' }}>
<h1>tRPC + Next.js + Zod Example</h1>
<div style={{ marginBottom: '0.5rem' }}>
<input
type="text"
value={name}
placeholder="Enter your name"
onChange={(e) => setName(e.target.value)}
/>
</div>
<button onClick={handleClick} disabled={loading}>
{loading ? 'Loading...' : 'Get Greeting'}
</button>
<div style={{ marginTop: '1rem' }}>
{result && <p>{result}</p>}
</div>
</main>
);
}
ブラウザからページを開き適当な文字を入力してGet Greetingを押すとapiが呼び出され入力した文字列が表示される。
これでフロントエンドとバックエンドの双方で型を共有しIDEの自動補完やAPI通信を型安全に行える。