trpc-router
About
This skill enables developers to add or modify tRPC routes with type-safe procedures and authentication. Use it when creating new API endpoints that require type safety or updating existing tRPC routes, including implementing input validation and protected procedures. It helps maintain tRPC's end-to-end type safety across your API and client applications.
Documentation
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
Quick Install
/plugin add https://github.com/sgcarstrends/sgcarstrends/tree/main/trpc-routerCopy and paste this command in Claude Code to install this skill
GitHub 仓库
Related Skills
evaluating-llms-harness
TestingThis 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.
langchain
MetaLangChain is a framework for building LLM applications using agents, chains, and RAG pipelines. It supports multiple LLM providers, offers 500+ integrations, and includes features like tool calling and memory management. Use it for rapid prototyping and deploying production systems like chatbots, autonomous agents, and question-answering services.
huggingface-accelerate
DevelopmentHuggingFace Accelerate provides the simplest API for adding distributed training to PyTorch scripts with just 4 lines of code. It offers a unified interface for multiple distributed training frameworks like DeepSpeed, FSDP, and DDP while handling automatic device placement and mixed precision. This makes it ideal for developers who want to quickly scale their PyTorch training across multiple GPUs or nodes without complex configuration.
nestjs
MetaThis skill provides NestJS development standards and architectural patterns for building domain-centric applications. It covers modular design, dependency injection, decorator patterns, and key framework features like controllers, services, middleware, and interceptors. Use it when developing NestJS applications, implementing APIs, configuring microservices, or integrating with databases.
