Use Cases

Desarrollo con tRPC y Claude Code

Aprenda sobre desarrollo con tRPC usando Claude Code. Incluye ejemplos practicos de codigo.

tRPCで型安全なAPI開発を実現する

tRPCはサーバーとクライアント間の型を自動共有するフレームワークです。Claude CodeはtRPCのルーター設計からプロシージャの実装まで、型安全な構成を一貫して生成してくれます。

基本的なルーター構成

Claude Codeにルーターの構成を依頼しましょう。

> tRPCのルーターを設計して。
> ユーザーCRUD、認証ミドルウェア、入力バリデーション付きで。
import { initTRPC, TRPCError } from "@trpc/server";
import { z } from "zod";

type Context = {
  session: { userId: string; role: string } | null;
  db: PrismaClient;
};

const t = initTRPC.context<Context>().create();

// ミドルウェア
const isAuthenticated = t.middleware(({ ctx, next }) => {
  if (!ctx.session) {
    throw new TRPCError({ code: "UNAUTHORIZED" });
  }
  return next({ ctx: { ...ctx, session: ctx.session } });
});

const protectedProcedure = t.procedure.use(isAuthenticated);

// ユーザールーター
export const userRouter = t.router({
  list: protectedProcedure
    .input(
      z.object({
        page: z.number().int().positive().default(1),
        perPage: z.number().int().min(1).max(100).default(20),
        search: z.string().optional(),
      })
    )
    .query(async ({ ctx, input }) => {
      const { page, perPage, search } = input;
      const where = search
        ? { name: { contains: search, mode: "insensitive" as const } }
        : {};

      const [users, total] = await Promise.all([
        ctx.db.user.findMany({
          where,
          skip: (page - 1) * perPage,
          take: perPage,
          orderBy: { createdAt: "desc" },
        }),
        ctx.db.user.count({ where }),
      ]);

      return { users, total, totalPages: Math.ceil(total / perPage) };
    }),

  getById: protectedProcedure
    .input(z.object({ id: z.string().uuid() }))
    .query(async ({ ctx, input }) => {
      const user = await ctx.db.user.findUnique({
        where: { id: input.id },
      });
      if (!user) throw new TRPCError({ code: "NOT_FOUND" });
      return user;
    }),

  create: protectedProcedure
    .input(
      z.object({
        name: z.string().min(2).max(50),
        email: z.string().email(),
        role: z.enum(["admin", "editor", "viewer"]),
      })
    )
    .mutation(async ({ ctx, input }) => {
      return ctx.db.user.create({ data: input });
    }),

  update: protectedProcedure
    .input(
      z.object({
        id: z.string().uuid(),
        name: z.string().min(2).max(50).optional(),
        email: z.string().email().optional(),
        role: z.enum(["admin", "editor", "viewer"]).optional(),
      })
    )
    .mutation(async ({ ctx, input }) => {
      const { id, ...data } = input;
      return ctx.db.user.update({ where: { id }, data });
    }),
});

クライアント側の型安全な呼び出し

tRPCの最大の利点は、クライアント側でサーバーの型がそのまま使えることです。

import { createTRPCReact } from "@trpc/react-query";
import type { AppRouter } from "../server/router";

export const trpc = createTRPCReact<AppRouter>();

function UserList() {
  const { data, isLoading } = trpc.user.list.useQuery({
    page: 1,
    perPage: 20,
  });

  const createUser = trpc.user.create.useMutation({
    onSuccess: () => {
      utils.user.list.invalidate();
    },
  });

  const utils = trpc.useUtils();

  if (isLoading) return <div>読み込み中...</div>;

  return (
    <div>
      {data?.users.map((user) => (
        <div key={user.id}>
          <span>{user.name}</span>
          <span>{user.email}</span>
        </div>
      ))}
    </div>
  );
}

エラーハンドリングの設計

Claude Codeにエラーハンドリングの仕組みも設計してもらいましょう。

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

// カスタムエラーフォーマッター
const t = initTRPC.context<Context>().create({
  errorFormatter({ shape, error }) {
    return {
      ...shape,
      data: {
        ...shape.data,
        zodError:
          error.cause instanceof ZodError ? error.cause.flatten() : null,
      },
    };
  },
});

// エラーをキャッチするミドルウェア
const errorHandler = t.middleware(async ({ next }) => {
  try {
    return await next();
  } catch (error) {
    if (error instanceof TRPCError) throw error;

    console.error("Unexpected error:", error);
    throw new TRPCError({
      code: "INTERNAL_SERVER_ERROR",
      message: "予期せぬエラーが発生しました",
    });
  }
});

サブスクリプション(リアルタイム通信)

WebSocket経由のリアルタイム通信もtRPCで型安全に実装できます。

import { observable } from "@trpc/server/observable";

export const notificationRouter = t.router({
  onNew: protectedProcedure.subscription(({ ctx }) => {
    return observable<Notification>((emit) => {
      const handler = (notification: Notification) => {
        if (notification.userId === ctx.session.userId) {
          emit.next(notification);
        }
      };

      eventEmitter.on("notification", handler);
      return () => eventEmitter.off("notification", handler);
    });
  }),
});

Summary

tRPCとClaude Codeの組み合わせは、型安全なフルスタック開発を最速で実現する手段です。Zodによる入力バリデーションとの統合で、ランタイムの安全性も同時に確保できます。

入力バリデーションの詳細はZodバリデーション実践ガイドを、クライアント側のデータ取得はTanStack Query活用ガイドを参照してください。tRPC公式ドキュメントも併せて確認しましょう。

#Claude Code #tRPC #TypeScript #API #type safety