Next.js + tRPC のチュートリアルをやってみる

概要

tRPC 公式の Next.js サンプルを読み解いていく。

  • Next.js v13
  • tRPC v10
Set up with Next.js | tRPC
Recommended file structure

以下のように hello client と表示されるだけのシンプルなアプリ。

tRPCざっくり

公式ドキュメントより全体像。src ディレクトリにまとめられているがどっちでも大丈夫。

.
├── prisma  # <-- if prisma is added
│   └── [..]
├── src
│   ├── pages
│   │   ├── _app.tsx  # <-- add `withTRPC()`-HOC here
│   │   ├── api
│   │   │   └── trpc
│   │   │       └── [trpc].ts  # <-- tRPC HTTP handler
│   │   └── [..]
│   ├── server
│   │   ├── routers
│   │   │   ├── _app.ts  # <-- main app router
│   │   │   ├── post.ts  # <-- sub routers
│   │   │   └── [..]
│   │   ├── context.ts   # <-- create app context
│   │   └── trpc.ts      # <-- procedure helpers
│   └── utils
│       └── trpc.ts  # <-- your typesafe tRPC hooks
└── [..]

サンプルで追加するファイル。

  • src/pages/_app.tsx : withTRPC() HOC を置くところ
  • src/pages/api/trpc/[trpc].ts : エンドポイントAPI(実態は routers 配下に実装)
  • src/server/routers/_app.ts : エンドポイントの実装
  • src/server/routers/posts.ts : 他のエンドポイントも routers 配下にに追加(サンプルでは未実装)
  • src/server/context.ts : プロシージャヘルパー(サンプルでは未実装)
  • src/server/trpc.ts : server 側 trpc 設定
  • src/utils/trpc.ts : client 側の trpc 設定

実装

tRPCエンドポイント作成 (Create a tRPC router)

server 側でエンドポイント API を作成していく。

はじめに initTRPC() を tRPC バックエンドを初期化する。

server/trpc.ts
import { TRPCError, initTRPC } from "@trpc/server";

// Avoid exporting the entire t-object
// since it's not very descriptive.
// For instance, the use of a t variable
// is common in i18n libraries.
const t = initTRPC.create();

// Base router and procedure helpers
export const router = t.router;
export const procedure = t.procedure;

「hello world」を返す API (router) を作成する。
Next.js の API Routes は通常 pages/api/ 配下に作成していくが、tRPC ルータは server/routers/ 配下に作成していく。

server/routers/_app.ts
import { z } from 'zod';
import { procedure, router } from '../trpc';
export const appRouter = router({
  hello: procedure
    .input(
      z.object({
        text: z.string(),
      }),
    )
    .query(({ input }) => {
      return {
        greeting: `hello ${input.text}`,
      };
    }),
});
// export type definition of API
export type AppRouter = typeof appRouter;

Next.js API Routes には tRPC ルータに委任するための設定ファイルを定義しておく。

pages/api/trpc/[trpc].ts
import * as trpcNext from '@trpc/server/adapters/next';
import { appRouter } from '../../../server/routers/_app';
// export API handler
export default trpcNext.createNextApiHandler({
  router: appRouter,
  createContext: () => ({}),
});

サーバ側はこれで完了。

tRPC クライアント作成 (Create tRPC hooks)

tRPCエンドポイントを呼び出すためのクライアントを設定する。

utils/trpc.ts
import { httpBatchLink } from '@trpc/client';
import { createTRPCNext } from '@trpc/next';
import type { AppRouter } from '../server/routers/_app';

function getBaseUrl() {
  if (typeof window !== 'undefined')
    // browser should use relative path
    return '';

  if (process.env.VERCEL_URL)
    // reference for vercel.com
    return `https://${process.env.VERCEL_URL}`;

  if (process.env.RENDER_INTERNAL_HOSTNAME)
    // reference for render.com
    return `http://${process.env.RENDER_INTERNAL_HOSTNAME}:${process.env.PORT}`;

  // assume localhost
  return `http://localhost:${process.env.PORT ?? 3000}`;
}

export const trpc = createTRPCNext<AppRouter>({
  config({ ctx }) {
    return {
      links: [
        httpBatchLink({
          /**
           * If you want to use SSR, you need to use the server's full URL
           * @link https://trpc.io/docs/ssr
           **/
          url: `${getBaseUrl()}/api/trpc`,
        }),
      ],
      /**
       * @link https://tanstack.com/query/v4/docs/reference/QueryClient
       **/
      // queryClientConfig: { defaultOptions: { queries: { staleTime: 60 } } },
    };
  },
  /**
   * @link https://trpc.io/docs/ssr
   **/
  ssr: false,
});

クライアント側はこれで完了。

_app.tsx に HOC 設定 (Configure _app.tsx)

withTRPC() でクライアント側アプリ(MyApp)を包むことで Next.js 全体(クライアント側・サーバ側両方)で trpc を呼び出せるように設定。

pages/_app.tsx
import type { AppType } from 'next/app';
import { trpc } from '../utils/trpc';
const MyApp: AppType = ({ Component, pageProps }) => {
  return <Component {...pageProps} />;
};
export default trpc.withTRPC(MyApp);

API を呼び出す (Make an API request)

Next.js のページから作成した tRPC エンドポイントを tRPC クライアント経由で呼び出す。

ここで使っている useQuery は react-query のもの。

pages/index.tsx
import { trpc } from '../utils/trpc';

export default function IndexPage() {
  const hello = trpc.hello.useQuery({ text: 'client' });
  if (!hello.data) {
    return <div>Loading...</div>;
  }
  return (
    <div>
      <p>{hello.data.greeting}</p>
    </div>
  );
}

参考

Set up with Next.js | tRPC
Recommended file structure
ふんわりざっくり大味で gRPC と tRPC の雰囲気を知る
tRPCを導入したら爆速でWebサービスをリリースできた話
FEELCAT 腹筋ローラー 静音 アブローラー スリムトレーナー アブホイール 上半身筋トレ 膝保護マット付き 耐荷重200kg
FEELCAT 腹筋ローラー 静音 アブローラー スリムトレーナー アブホイール 上半身筋トレ 膝保護マット付き 耐荷重200kg
JEAU CHAU 100% ヒハツパウダー 100g (インド産 ヒハツ) 選別品 ロングペッパー スパイス 香辛料/無添加 無農薬
丁寧に選別した無添加のヒハツパウダーです。 スパイスは香りが命。加工方法も大事ですが、何より鮮度が重要です。 JEAU CHAU(ジョウショー)では、現地から新鮮なスパイスを調達し、素早くパッキング。 自信をもってオススメできる、素晴らしい...
明治 チョコレート効果カカオ95%大袋 180g
●美と健康を考えたチョコレート効果から小袋タイプが新登場! ●まだ食べたことがない人にオススメのトライアルタイプです。 ●明治は、赤ちゃんからお年寄りまで、いろいろなシーンでいつもあなたのそばにありたい。おいしさであなたとずっとつながってい...
Bitly
Bitly
YUBOEST ホットアイマスク USB 充電式 【コードレス】「純シルク製&極上リラックス」軽量 かわいい 持ち運び便利 タイマー設定 温度調節 繰り返し使用可能 家族 ギフトケース包装 (ネイビー)
YUBOEST ホットアイマスク USB 充電式 アイマスク【コードレス】軽量 かわいい シルク 持ち運び便利 タイマー設定 温度調節 繰り返し使用可能 恋人 家族 父の日 恋人 母 誕生日に プレゼント 日語説明書 ギフトケース包装 (ネ...

コメント

タイトルとURLをコピーしました