Use Cases

Claude Code के साथ tRPC API

Claude Code का उपयोग करके trpc api सीखें। Practical tips और code examples शामिल हैं।

tRPCで型safeなAPIdevelopmentを実現する

tRPCはserverとclientबीचの型を自動共有するframework है।Claude CodeはtRPCのルーター設計 सेプロシージャのimplementation तक、型safeな構成を一貫してgenerateしてくれ है।

basic ルーター構成

Claude Codeにルーターの構成を依頼 करें।

> tRPCのルーターを設計して。
> userCRUD、authenticationmiddleware、入力validation付きで。
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();

// middleware
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);

// userルーター
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 });
    }),
});

client側の型safeな呼び出し

tRPCの最大の利点は、client側でserverの型がउसまま使える बात है।

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>
  );
}

error handlingの設計

Claude Codeにerror handlingの仕組みも設計してもらいましょう。

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

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

// errorをキャッチするmiddleware
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: "予期せぬerrorが発生しました",
    });
  }
});

subscription(リアルタイム通信)

WebSocket経由のリアルタイム通信もtRPCで型safeにimplementationでき है।

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の組み合わせは、型safeなフルスタックdevelopmentを最速で実現する手段 है।Zodによる入力validationとのintegrationで、runtimeのsafe性も同時に確保でき है।

入力validationके details के लिएZodvalidation実践ガイドを、client側のdatafetchはTanStack Queryutilizationガイドをदेखें。tRPCofficial documentationも併せてconfirm करें।

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