Does NestJS Work With tRPC?
You can use tRPC with NestJS, but they solve overlapping problems and require deliberate architectural choices to avoid redundancy.
Quick Facts
How NestJS Works With tRPC
NestJS and tRPC can coexist, but their design philosophies create tension. NestJS is a full framework with dependency injection, decorators, and built-in validation, while tRPC is a lightweight RPC library focused on type safety without schema files. The typical approach is to use NestJS as your HTTP server/DI container and expose tRPC routers as endpoints, or use NestJS controllers alongside separate tRPC procedures. However, this creates duplication: you're managing request validation, error handling, and middleware in both systems. A cleaner integration uses tRPC purely for client-side type safety, bypassing NestJS controllers entirely for that API surface. Developers often find themselves choosing between frameworks—using only NestJS's native capabilities or migrating entirely to tRPC with a simpler Node backend. If you need NestJS's ecosystem (guards, interceptors, dependency injection), tRPC adds minimal value. If you want tRPC's zero-codegen DX, NestJS's opinionated structure feels heavyweight.
Best Use Cases
Quick Setup
npm install @nestjs/core @nestjs/common @trpc/server zodimport { Controller, Post, Body } from '@nestjs/common';
import { initTRPC } from '@trpc/server';
import { z } from 'zod';
const t = initTRPC.create();
const appRouter = t.router({
greet: t.procedure
.input(z.object({ name: z.string() }))
.query(({ input }) => ({ message: `Hello ${input.name}` })),
});
export type AppRouter = typeof appRouter;
@Controller('trpc')
export class TrpcController {
@Post('greet')
async handleTrpc(@Body() body: { name: string }) {
const caller = appRouter.createCaller({});
return caller.greet({ name: body.name });
}
}Known Issues & Gotchas
Duplicate validation logic between NestJS decorators (class-validator) and tRPC zod/yup schemas
Fix: Choose one validation system. Use tRPC's Zod validators throughout and access them in NestJS via middleware, or use NestJS pipes exclusively and skip tRPC validation.
Conflicting middleware/interceptor patterns make request lifecycle unclear
Fix: Establish a clear boundary: use NestJS interceptors for HTTP concerns (logging, error formatting) and tRPC middleware for procedure-level logic.
tRPC's built-in error handling doesn't integrate with NestJS exception filters
Fix: Create a custom tRPC error formatter that aligns with your NestJS error response shape, or route tRPC errors through NestJS exception handling.
Adding tRPC to an existing NestJS project increases bundle size and maintenance surface area without clear ROI
Fix: Evaluate if you need tRPC's features; NestJS 9+ with Swagger/OpenAPI provides similar DX for type generation.
Alternatives
- •NestJS + OpenAPI/Swagger: Use NestJS exclusively with Swagger for API documentation and type generation without adding tRPC.
- •Express + tRPC: Skip NestJS entirely and use Express for simpler routing, letting tRPC handle all RPC logic and type safety.
- •Fastify + tRPC: Use Fastify's lightweight HTTP server directly with tRPC for maximum performance and minimal framework overhead.
Resources
Related Compatibility Guides
Explore more compatibility guides