mcpField
T
trpc

trpc

github.com/mindrally開発287872 回インストール3 週間前に更新
リポジトリ

trpc について

tRPC v11、TypeScript、Next.js でエンドツーエンドの型安全な API を構築するためのガイダンススキルです。サーバーとクライアントの設定、検証、認可、React Query 連携を扱います。

主な機能

  1. 01エンドツーエンドの型推論
  2. 02Zod による入力検証
  3. 03機能別ルーター構成
  4. 04ミドルウェアと認可パターン
  5. 05React Query のデータ機能

活用シーン

  1. 01ドメイン別の tRPC API 構成
  2. 02プロシージャ入力の検証
  3. 03保護プロシージャとミドルウェアの追加
  4. 04React でのデータ取得と変更

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/mindrally/skills --skill trpc

インストール後、Claude Code で/trpcを入力して読み込まれたか確認します。

詳細

tRPC のベストプラクティス

tRPC v11、TypeScript、Next.js 開発のエキスパートです。tRPC を使うと、スキーマやコード生成、実行時エラーを必要とせず、エンドツーエンドで型安全な API を構築して利用できます。

要件

  • TypeScript >= 5.7.2
  • TypeScript の厳格モードを有効にする

プロジェクト構成

src/
  pages/
    _app.tsx              # createTRPCNext setup
    api/
      trpc/
        [trpc].ts         # tRPC HTTP handler
  server/
    routers/
      _app.ts             # Main router
      [feature].ts        # Feature-specific routers
    context.ts            # App context
    trpc.ts               # Procedure helpers
  utils/
    trpc.ts               # Typesafe React hooks

サーバー側のセットアップ

tRPC バックエンドの初期化

アプリケーションごとに一度だけ tRPC バックエンドを初期化します。再利用可能なルーターとプロシージャのヘルパーをエクスポートします。

// server/trpc.ts
import { initTRPC } from '@trpc/server';
import superjson from 'superjson';

const t = initTRPC.context<Context>().create({
  transformer: superjson,
});

export const router = t.router;
export const publicProcedure = t.procedure;
export const protectedProcedure = t.procedure.use(isAuthed);
export const adminProcedure = t.procedure.use(isAdmin);
機能ごとのルーターの作成

ルーターを機能/ドメインごとに整理します。入力のバリデーションには Zod を使用します。

// server/routers/user.ts
import { z } from 'zod';
import { router, publicProcedure, protectedProcedure } from '../trpc';

export const userRouter = router({
  getById: publicProcedure
    .input(z.object({ id: z.string() }))
    .query(async ({ input, ctx }) => {
      return ctx.db.user.findUnique({ where: { id: input.id } });
    }),

  update: protectedProcedure
    .input(z.object({
      name: z.string().min(1),
      email: z.string().email(),
    }))
    .mutation(async ({ input, ctx }) => {
      return ctx.db.user.update({
        where: { id: ctx.user.id },
        data: input,
      });
    }),
});
メインアプリルーター
// server/routers/_app.ts
import { router } from '../trpc';
import { userRouter } from './user';
import { postRouter } from './post';

export const appRouter = router({
  user: userRouter,
  post: postRouter,
});

export type AppRouter = typeof appRouter;

クライアント側のセットアップ

tRPC クライアントの設定
// utils/trpc.ts
import { httpBatchLink } from '@trpc/client';
import { createTRPCNext } from '@trpc/next';
import superjson from 'superjson';
import type { AppRouter } from '../server/routers/_app';

function getBaseUrl() {
  if (typeof window !== 'undefined') return '';
  if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`;
  return `http://localhost:${process.env.PORT ?? 3000}`;
}

export const trpc = createTRPCNext<AppRouter>({
  config() {
    return {
      transformer: superjson,
      links: [
        httpBatchLink({
          url: `${getBaseUrl()}/api/trpc`,
          maxURLLength: 2083,
        }),
      ],
    };
  },
  ssr: false,
});

主なベストプラクティス

1. 入力のバリデーション

型安全性を確保し、すべてのプロシージャ入力を実行時にバリデーションするため、必ず Zod を使用します。

.input(z.object({
  email: z.string().email(),
  age: z.number().min(0).max(120),
}))
2. ルーターの構成

単一の巨大なルーターではなく、機能/ドメインごとにルーターを構成します。各機能には専用のルーターファイルを用意します。

3. ミドルウェアの実装

認証、ロギング、横断的な関心事に対応するミドルウェアを実装します。

const isAuthed = t.middleware(({ ctx, next }) => {
  if (!ctx.user) {
    throw new TRPCError({ code: 'UNAUTHORIZED' });
  }
  return next({ ctx: { user: ctx.user } });
});
4. エラー処理

一貫性があり、情報が明確なエラーレスポンスを返すには、TRPCError を使用します。

import { TRPCError } from '@trpc/server';

throw new TRPCError({
  code: 'NOT_FOUND',
  message: 'User not found',
});
5. データトランスフォーマー

日付、Map、Set、その他の JavaScript 型を自動的にシリアライズするため、SuperJSON を適用します。

import superjson from 'superjson';

const t = initTRPC.create({
  transformer: superjson,
});
6. React Query との統合

データ取得、ミューテーション、キャッシュには、tRPC に組み込まれている React Query ユーティリティを活用します。

// Queries
const { data, isLoading } = trpc.user.getById.useQuery({ id: '123' });

// Mutations
const mutation = trpc.user.update.useMutation({
  onSuccess: () => {
    utils.user.getById.invalidate();
  },
});

// Prefetching
await utils.user.getById.prefetch({ id: '123' });
7. コンテキストの作成

コンテキストを介して、リソース(データベース接続、ユーザーセッション)をプロシージャ間で共有します。

// server/context.ts
export async function createContext({ req, res }: CreateNextContextOptions) {
  const user = await getUser(req);
  return {
    db: prisma,
    user,
    req,
    res,
  };
}

export type Context = Awaited<ReturnType<typeof createContext>>;
8. 型のエクスポート

クライアントコードには型シグネチャのみをエクスポートし、ルーターの実装は決してエクスポートしません。

// Only export the type
export type AppRouter = typeof appRouter;

// Never export the actual router to client code
9. 認可レベル

認可レベルごとに異なるプロシージャ型を作成します。

export const publicProcedure = t.procedure;
export const protectedProcedure = t.procedure.use(isAuthed);
export const adminProcedure = t.procedure.use(isAuthed).use(isAdmin);
10. パフォーマンスの最適化
  • httpBatchLink でリクエストのバッチ処理を有効にする
  • URL の長さに関する問題を防ぐため、maxURLLength を設定する
  • 静的データや予測可能なデータに対してプリフェッチを実装する
  • React Query のキャッシュ戦略を使用する

避けるべきアンチパターン

  • any 型を使用しないでください。tRPC の完全な型推論を活用します
  • 入力のバリデーションを省略しないでください
  • 内部エラーをクライアントに公開しないでください
  • サーバーコードとクライアントコードを混在させないでください
  • 巨大すぎるルーターを作成しないでください。機能ごとに分割します

コメント