tRPC End-to-End Typesafe APIs
Type-safe remote procedure calls with full client-server type inference. No code generation needed. tRPC excels at:
- Zero API generation — Shared types between client and server; type-safe mutations/queries by default
- Middleware composition — Chain middlewares with
unstable_pipe() for authentication, logging, authorization
- Input validation — Zod schema validation with auto-formatted error responses
- Error handling — Domain-specific
TRPCError codes mapped to HTTP status automatically
- Vertical Slice pattern — Organize by feature (slice), not layer; each slice owns procedures, schemas, data access
Core patterns: procedures (queries/mutations) in routers, input/output schemas for contracts, middleware for cross-cutting concerns, domain errors with proper codes.
Workflow
When designing tRPC APIs:
- Define the slice — Which feature? (e.g.,
invitations, payments, users)
- Schema first — Input schema with Zod, output schema for response contract
- Create procedures — Query, mutation, or subscription with
.procedure.input().query()
- Add middleware — Authentication, logging, authorization; chain with
unstable_pipe()
- Error handling — Throw
TRPCError with domain-specific codes, not plain Error
- Organize — One file per procedure; repository per slice; no cross-slice imports
Rules
Patterns are organized by concern:
- Architecture — Vertical Slice pattern, one procedure per file, feature isolation
- Procedures — Query, mutation, subscription; procedure hierarchy and composition
- Schemas — Zod input/output validation; always define schemas
- Middleware — Authentication, logging, context enrichment; unstable_pipe chaining
- Error Handling — Domain-specific errors, proper TRPCError codes, error formatting
- Data Access — Repository pattern per slice; data layer isolation
See rules/ for implementation patterns and examples.
Examples
Positive Trigger
User: "Add a tRPC mutation for creating invoices with Zod input validation."
Expected behavior: Use tech-trpc guidance, apply procedure design with Zod schema and proper error handling.
Non-Trigger
User: "Write a Python Flask endpoint for file uploads."
Expected behavior: Do not prioritize tech-trpc; choose a more relevant skill or proceed without it.
Error: Throwing plain Error in procedures
Cause: Plain errors become INTERNAL_SERVER_ERROR (500); clients cannot distinguish errors
Solution: Throw TRPCError with domain code: throw new TRPCError({ code: 'NOT_FOUND', message: '...' })
Error: Middlewares not chaining properly; context lost
Cause: Middlewares not piped with unstable_pipe(); context isolation between middleware layers
Solution: Use .unstable_pipe() to chain middleware and extend context
Troubleshooting
Error: Client receives generic "Input validation failed"
Cause: No custom errorFormatter configured; Zod errors not sent to client
Solution: Configure initTRPC.create({ errorFormatter }) to flatten Zod errors in response
Error: Middleware context not accessible in procedure
Cause: Middleware not piped or context not merged in opts.next()
Solution: Ensure middleware returns opts.next({ ctx: { ...prevCtx, newKey: value } })
Error: Procedure types leak into client; can import server code
Cause: Exporting procedure instance instead of type; typeof appRouter not used
Solution: Export only type AppRouter = typeof appRouter; client imports type only
1---2name: tech-trpc3description: tRPC end-to-end typesafe APIs. Router architecture, procedure design, middleware chaining, input validation with Zod, error handling, and Vertical Slice Architecture. Use when building tRPC APIs, designing procedures, structuring backend slices, or handling cross-procedure logic.4---56# tRPC End-to-End Typesafe APIs78Type-safe remote procedure calls with full client-server type inference. No code generation needed. tRPC excels at:9- **Zero API generation** — Shared types between client and server; type-safe mutations/queries by default10- **Middleware composition** — Chain middlewares with `unstable_pipe()` for authentication, logging, authorization11- **Input validation** — Zod schema validation with auto-formatted error responses12- **Error handling** — Domain-specific `TRPCError` codes mapped to HTTP status automatically13- **Vertical Slice pattern** — Organize by feature (slice), not layer; each slice owns procedures, schemas, data access1415Core patterns: procedures (queries/mutations) in routers, input/output schemas for contracts, middleware for cross-cutting concerns, domain errors with proper codes.1617## Workflow1819When designing tRPC APIs:20211. **Define the slice** — Which feature? (e.g., `invitations`, `payments`, `users`)222. **Schema first** — Input schema with Zod, output schema for response contract233. **Create procedures** — Query, mutation, or subscription with `.procedure.input().query()`244. **Add middleware** — Authentication, logging, authorization; chain with `unstable_pipe()`255. **Error handling** — Throw `TRPCError` with domain-specific codes, not plain Error266. **Organize** — One file per procedure; repository per slice; no cross-slice imports2728## Rules2930Patterns are organized by concern:3132- **Architecture** — Vertical Slice pattern, one procedure per file, feature isolation33- **Procedures** — Query, mutation, subscription; procedure hierarchy and composition34- **Schemas** — Zod input/output validation; always define schemas35- **Middleware** — Authentication, logging, context enrichment; unstable_pipe chaining36- **Error Handling** — Domain-specific errors, proper TRPCError codes, error formatting37- **Data Access** — Repository pattern per slice; data layer isolation3839See `rules/` for implementation patterns and examples.4041## Examples4243### Positive Trigger4445User: "Add a tRPC mutation for creating invoices with Zod input validation."4647Expected behavior: Use `tech-trpc` guidance, apply procedure design with Zod schema and proper error handling.4849### Non-Trigger5051User: "Write a Python Flask endpoint for file uploads."5253Expected behavior: Do not prioritize `tech-trpc`; choose a more relevant skill or proceed without it.5455- Error: Throwing plain Error in procedures56- Cause: Plain errors become INTERNAL_SERVER_ERROR (500); clients cannot distinguish errors57- Solution: Throw `TRPCError` with domain code: `throw new TRPCError({ code: 'NOT_FOUND', message: '...' })`5859- Error: Middlewares not chaining properly; context lost60- Cause: Middlewares not piped with `unstable_pipe()`; context isolation between middleware layers61- Solution: Use `.unstable_pipe()` to chain middleware and extend context6263## Troubleshooting6465- Error: Client receives generic "Input validation failed"66- Cause: No custom `errorFormatter` configured; Zod errors not sent to client67- Solution: Configure `initTRPC.create({ errorFormatter })` to flatten Zod errors in response6869- Error: Middleware context not accessible in procedure70- Cause: Middleware not piped or context not merged in `opts.next()`71- Solution: Ensure middleware returns `opts.next({ ctx: { ...prevCtx, newKey: value } })`7273- Error: Procedure types leak into client; can import server code74- Cause: Exporting procedure instance instead of type; `typeof appRouter` not used75- Solution: Export only `type AppRouter = typeof appRouter`; client imports type only