NestJS 11 Best Practices
Quick Reference
Topic When to Use Reference
Core Architecture Modules, Providers, DI, forwardRef, custom decorators core-architecture.md
Request Lifecycle Middleware, Guards, Interceptors, Pipes, Filters request-lifecycle.md
Validation & Pipes DTOs, class-validator, ValidationPipe, transforms validation-pipes.md
Authentication JWT, Passport, Guards, Local/OAuth strategies, RBAC authentication.md
Database TypeORM, Prisma, Drizzle ORM, repository patterns database-integration.md
Testing Unit tests, E2E tests, mocking providers testing.md
OpenAPI & GraphQL Swagger decorators, resolvers, subscriptions openapi-graphql.md
Microservices TCP, Redis, NATS, Kafka patterns microservices.md
Essential Patterns
Module with Providers
@Module({ imports: [DatabaseModule], controllers: [UsersController], providers: [UsersService], exports: [UsersService], // Export for other modules }) export class UsersModule {}
Controller with Validation
@Controller('users') export class UsersController { constructor(private readonly usersService: UsersService) {}
@Post() create(@Body() createUserDto: CreateUserDto) { return this.usersService.create(createUserDto); }
@Get(':id') findOne(@Param('id', ParseIntPipe) id: number) { return this.usersService.findOne(id); } }
DTO with Validation
import { IsEmail, IsString, MinLength, IsOptional } from 'class-validator';
export class CreateUserDto { @IsEmail() email: string;
@IsString() @MinLength(8) password: string;
@IsOptional() @IsString() name?: string; }
Exception Filter
@Catch(HttpException) export class HttpExceptionFilter implements ExceptionFilter { catch(exception: HttpException, host: ArgumentsHost) { const ctx = host.switchToHttp(); const response = ctx.getResponse<Response>(); const status = exception.getStatus();
response.status(status).json({
statusCode: status,
message: exception.message,
timestamp: new Date().toISOString(),
});
} }
Guard with JWT
@Injectable() export class JwtAuthGuard extends AuthGuard('jwt') { canActivate(context: ExecutionContext) { return super.canActivate(context); } }
NestJS 11 Breaking Changes
-
Express v5: Wildcards must be named (e.g., *splat ), optional params use braces /:file{.:ext}
-
Node.js 20+: Minimum required version
-
Fastify v5: Updated adapter for Fastify users
-
Dynamic Modules: Same module with identical config imported multiple times = separate instances
Common Mistakes
-
Not using forwardRef() for circular deps - Causes "cannot resolve dependency" errors; wrap in forwardRef(() => ModuleName)
-
Throwing plain errors instead of HttpException - Loses status codes, breaks exception filters; use throw new BadRequestException('message')
-
Missing @Injectable() decorator - Provider won't be injectable; always decorate services
-
Global ValidationPipe without whitelist: true
-
Allows unexpected properties; set whitelist: true, forbidNonWhitelisted: true
-
Importing modules instead of exporting providers - Use exports array to share providers across modules
-
Async config without ConfigModule.forRoot()
-
ConfigService undefined; import ConfigModule in AppModule
-
Testing without overrideProvider()
-
Uses real services in unit tests; mock dependencies with overrideProvider(Service).useValue(mock)
-
E2E tests sharing database state - No isolation between tests; use transactions or truncate tables in beforeEach