0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Next.js + tRPCをApp Routerで使う

Posted at

この記事では、 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ルーターの作成

server/trpc.ts
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 関数を定義します。

server/index.ts
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 を定義します。

app/api/trpc/[trpc]/route.ts
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 クライアントの作成を行います。

components/client.ts
import { AppRouter } from "@/server";
import { createTRPCClient, httpBatchLink } from "@trpc/client";

export const client = createTRPCClient<AppRouter>({
  links: [
    httpBatchLink({
      url: "/api/trpc",
    }),
  ],
});

呼び出しを行うクライアントコンポーネントを定義します。

components/UserClient.tsx
"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 を作成する必要があります。

components/server.ts
import { appRouter } from "@/server";

const createContext = () => ({});

export const server = appRouter.createCaller(createContext());

呼び出しを行うサーバーコンポーネントを定義します。

components/UserServer.tsx
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>;
}

コンポーネントを表示するページの実装

UserClientUserServerを呼び出すためのページを作成します。

app/page.tsx
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...と表示され、

loading

ローディング後には値が表示されました!

loaded

まとめ

この記事では、Next.js と tRPC を使って API を構築する基本的な流れを紹介しました。
セットアップからサーバー・クライアントの実装、コンポーネントでのデータ取得まで、一通りの使い方を見てきました。

tRPC を使えば、API の型安全を保ちつつ、シンプルな開発が可能になります。
Next.js との相性もよく、面倒なエンドポイントの定義や API の型定義を最小限に抑えられます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?