この記事では、 Next.js と tRPC を使ってアプリケーションをセットアップする方法について解説します。クライアントコンポーネントとサーバーコンポーネントの両方で tRPC を活用する方法を紹介します。
環境構築
Next.jsのセットアップ
npx create-next-app@latest
必要なパッケージのインストール
npm i @trpc/server@next @trpc/client@next swr
ここでは、tRPC v11(プレリリース版)と、クライアント側のデータ取得に SWR を使用します。
tRPCサーバーの実装
基本的には、tRPC Quickstart にそって行います。
tRPCルーターの作成
import { initTRPC } from '@trpc/server';
/**
* Initialization of tRPC backend
* Should be done only once per backend!
*/
const t = initTRPC.create();
/**
* Export reusable router and procedure helpers
* that can be used throughout the router
*/
export const router = t.router;
export const publicProcedure = t.procedure;
API エンドポイントの定義
サンプルとして getUser 関数を定義します。
import { setTimeout } from "node:timers/promises";
import { publicProcedure, router } from "./trpc";
const getUser = async () => {
// mock delay
await setTimeout(1000);
return { name: "User" };
};
export const appRouter = router({
getUser: publicProcedure.query(async () => {
const users = await getUser();
return users;
}),
});
// Export type router type signature,
// NOT the router itself.
export type AppRouter = typeof appRouter;
Route Handlersの作成
Next.jsのRoute Handlers機能を用いて、以下の handler を定義します。
import { appRouter } from "@/server";
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
function handler(req: Request) {
return fetchRequestHandler({
endpoint: "/api/trpc",
req,
router: appRouter,
createContext: () => ({}),
});
}
export { handler as GET, handler as POST };
クライアントコンポーネントでの tRPC 呼び出し
tRPC クライアントの作成を行います。
import { AppRouter } from "@/server";
import { createTRPCClient, httpBatchLink } from "@trpc/client";
export const client = createTRPCClient<AppRouter>({
links: [
httpBatchLink({
url: "/api/trpc",
}),
],
});
呼び出しを行うクライアントコンポーネントを定義します。
"use client";
import { client } from "./client";
import useSWR from "swr";
export default function UserClient() {
const { data } = useSWR("userList", client.getUser.query);
if (data === undefined) {
return <span>Loading...</span>;
}
return <span>UserClient: {data.name}</span>;
}
サーバーコンポーネントでの tRPC 呼び出し
tRPC クライアントを作成します。
サーバー側から直接呼び出す場合は、Request を通さずに実行するため、独自の Context を作成する必要があります。
import { appRouter } from "@/server";
const createContext = () => ({});
export const server = appRouter.createCaller(createContext());
呼び出しを行うサーバーコンポーネントを定義します。
import { Suspense } from "react";
import { server } from "./server";
export default async function UserServer() {
return (
<Suspense fallback={<span>Loading...</span>}>
<AsyncComponent />
</Suspense>
);
}
async function AsyncComponent() {
const data = await server.getUser();
return <span>UserServer: {data.name}</span>;
}
コンポーネントを表示するページの実装
UserClient
、UserServer
を呼び出すためのページを作成します。
import styles from "./page.module.css";
import UserClient from "./components/UserClient";
import UserServer from "./components/UserServer";
export default function Home() {
return (
<div className={styles.page}>
<main className={styles.main}>
<UserClient />
<UserServer />
</main>
</div>
);
}
実行してみる
開発サーバーを起動します。
npm run dev
どちらもローディング中にはLoading...と表示され、
ローディング後には値が表示されました!
まとめ
この記事では、Next.js と tRPC を使って API を構築する基本的な流れを紹介しました。
セットアップからサーバー・クライアントの実装、コンポーネントでのデータ取得まで、一通りの使い方を見てきました。
tRPC を使えば、API の型安全を保ちつつ、シンプルな開発が可能になります。
Next.js との相性もよく、面倒なエンドポイントの定義や API の型定義を最小限に抑えられます。