trpc-router
について
このスキルにより、開発者は型安全な手続きと認証を備えたtRPCルートの追加や修正が可能になります。型安全性が必要な新しいAPIエンドポイントの作成や、入力検証や保護された手続きの実装を含む既存のtRPCルートの更新時にご利用ください。APIとクライアントアプリケーション間でtRPCのエンドツーエンド型安全性を維持するのに役立ちます。
クイックインストール
Claude Code
推奨/plugin add https://github.com/sgcarstrends/sgcarstrendsgit clone https://github.com/sgcarstrends/sgcarstrends.git ~/.claude/skills/trpc-routerこのコマンドをClaude Codeにコピー&ペーストしてスキルをインストールします
ドキュメント
tRPC Router Skill
This skill helps you work with tRPC routes in apps/api/src/trpc/.
When to Use This Skill
- Creating new type-safe API endpoints
- Adding authentication/authorization to routes
- Implementing input validation with Zod
- Creating public vs. protected procedures
- Debugging tRPC errors or type issues
tRPC Architecture
tRPC provides end-to-end type safety between API and client:
apps/api/src/trpc/
├── root.ts # Root router combining all routers
├── trpc.ts # tRPC context and procedure definitions
└── routers/ # Individual feature routers
├── cars.ts
├── coe.ts
└── posts.ts
Key Patterns
1. Creating a New Router
import { z } from "zod";
import { createTRPCRouter, publicProcedure, protectedProcedure } from "../trpc";
export const myRouter = createTRPCRouter({
// Public endpoint (no auth required)
getAll: publicProcedure
.input(z.object({
limit: z.number().min(1).max(100).default(10),
offset: z.number().min(0).default(0),
}))
.query(async ({ input, ctx }) => {
const { limit, offset } = input;
// Query database using ctx.db
return await ctx.db.query.myTable.findMany({
limit,
offset,
});
}),
// Protected endpoint (requires authentication)
create: protectedProcedure
.input(z.object({
name: z.string().min(1),
description: z.string().optional(),
}))
.mutation(async ({ input, ctx }) => {
// ctx.user is available in protected procedures
return await ctx.db.insert(myTable).values({
...input,
userId: ctx.user.id,
});
}),
});
2. Input Validation with Zod
Always validate inputs using Zod schemas:
import { z } from "zod";
const carInputSchema = z.object({
make: z.string().min(1).max(100),
model: z.string().min(1).max(100),
year: z.number().int().min(1900).max(new Date().getFullYear() + 1),
price: z.number().positive().optional(),
});
export const carsRouter = createTRPCRouter({
create: protectedProcedure
.input(carInputSchema)
.mutation(async ({ input, ctx }) => {
// Input is fully typed and validated
return await ctx.db.insert(cars).values(input);
}),
});
3. Procedure Types
Query - For reading data (GET-like):
getById: publicProcedure
.input(z.object({ id: z.string() }))
.query(async ({ input }) => {
// Return data
})
Mutation - For writing data (POST/PUT/DELETE-like):
update: protectedProcedure
.input(z.object({ id: z.string(), name: z.string() }))
.mutation(async ({ input }) => {
// Modify data
})
Subscription - For real-time updates (WebSocket):
onUpdate: publicProcedure
.subscription(() => {
// Stream updates
})
Common Tasks
Adding a New Router
- Create router file in
apps/api/src/trpc/routers/ - Define procedures with input validation
- Import and add to root router in
apps/api/src/trpc/root.ts:import { myRouter } from "./routers/my-router"; export const appRouter = createTRPCRouter({ cars: carsRouter, coe: coeRouter, my: myRouter, // Add here }); - Types are automatically available in client apps
Adding Authentication
Use protectedProcedure for authenticated routes:
import { protectedProcedure } from "../trpc";
export const adminRouter = createTRPCRouter({
dangerousOperation: protectedProcedure
.mutation(async ({ ctx }) => {
// ctx.user is guaranteed to exist
console.log("User:", ctx.user.email);
// Perform operation
}),
});
Error Handling
Throw tRPC errors with appropriate codes:
import { TRPCError } from "@trpc/server";
export const carsRouter = createTRPCRouter({
getById: publicProcedure
.input(z.object({ id: z.string() }))
.query(async ({ input, ctx }) => {
const car = await ctx.db.query.cars.findFirst({
where: eq(cars.id, input.id),
});
if (!car) {
throw new TRPCError({
code: "NOT_FOUND",
message: `Car with id ${input.id} not found`,
});
}
return car;
}),
});
Error codes:
BAD_REQUEST- Invalid inputUNAUTHORIZED- Not authenticatedFORBIDDEN- Not authorizedNOT_FOUND- Resource not foundINTERNAL_SERVER_ERROR- Server error
Context Access
The ctx parameter provides access to:
ctx.db- Drizzle database instancectx.redis- Redis clientctx.user- Current user (in protectedProcedure)ctx.req- HTTP request objectctx.res- HTTP response object
Type Safety
tRPC automatically generates TypeScript types:
// In client code (e.g., Next.js app)
import { trpc } from "@/lib/trpc";
// Fully typed, autocomplete works!
const { data, isLoading } = trpc.cars.getAll.useQuery({
limit: 10,
offset: 0,
});
// Type error if input is wrong
const { mutate } = trpc.cars.create.useMutation();
mutate({
make: "Toyota",
// Error: missing required field 'model'
});
Testing tRPC Routes
Create tests in apps/api/src/trpc/routers/__tests__/:
import { describe, it, expect } from "vitest";
import { createCaller } from "../../trpc";
import { appRouter } from "../../root";
describe("cars router", () => {
it("should get all cars", async () => {
const caller = createCaller(appRouter);
const result = await caller.cars.getAll({ limit: 10, offset: 0 });
expect(result).toBeDefined();
expect(Array.isArray(result)).toBe(true);
});
});
Run tests:
pnpm -F @sgcarstrends/api test -- src/trpc
References
- tRPC Documentation: Use Context7 to get latest tRPC docs
- Related files:
apps/api/src/trpc/trpc.ts- Procedure definitionsapps/api/src/trpc/root.ts- Root routerapps/api/CLAUDE.md- API service documentation
Best Practices
- Validation: Always use Zod for input validation
- Type Safety: Let TypeScript infer types, avoid manual typing
- Error Handling: Use appropriate tRPC error codes
- Security: Use
protectedProcedurefor sensitive operations - Naming: Use clear, RESTful names (get, getById, create, update, delete)
- Testing: Write tests for all procedures
- Documentation: Add JSDoc comments for complex logic
GitHub リポジトリ
関連スキル
creating-opencode-plugins
メタThis skill provides the structure and API specifications for creating OpenCode plugins that hook into 25+ event types like commands, files, and LSP operations. It offers implementation patterns for JavaScript/TypeScript modules that intercept and extend the AI assistant's lifecycle. Use it when you need to build event-driven plugins for monitoring, custom handling, or extending OpenCode's capabilities.
evaluating-llms-harness
テストThis Claude Skill runs the lm-evaluation-harness to benchmark LLMs across 60+ standardized academic tasks like MMLU and GSM8K. It's designed for developers to compare model quality, track training progress, or report academic results. The tool supports various backends including HuggingFace and vLLM models.
polymarket
メタThis skill enables developers to build applications with the Polymarket prediction markets platform, including API integration for trading and market data. It also provides real-time data streaming via WebSocket to monitor live trades and market activity. Use it for implementing trading strategies or creating tools that process live market updates.
cloudflare-turnstile
メタThis skill provides comprehensive guidance for implementing Cloudflare Turnstile as a CAPTCHA-alternative bot protection system. It covers integration for forms, login pages, API endpoints, and frameworks like React/Next.js/Hono, while handling invisible challenges that maintain user experience. Use it when migrating from reCAPTCHA, debugging error codes, or implementing token validation and E2E tests.
