NestJS Core Knowledge
Deep Knowledge: Use mcp__documentation__fetch_docs with technology: nestjs for comprehensive documentation.
Module Structure
// users.module.ts @Module({ imports: [TypeOrmModule.forFeature([User])], controllers: [UsersController], providers: [UsersService], exports: [UsersService], }) export class UsersModule {}
Controller
@Controller('users') export class UsersController { constructor(private readonly usersService: UsersService) {}
@Get() findAll(): Promise<User[]> { return this.usersService.findAll(); }
@Get(':id') findOne(@Param('id') id: string): Promise<User> { return this.usersService.findOne(id); }
@Post() @HttpCode(201) create(@Body() createUserDto: CreateUserDto): Promise<User> { return this.usersService.create(createUserDto); }
@Put(':id') update(@Param('id') id: string, @Body() dto: UpdateUserDto) { return this.usersService.update(id, dto); }
@Delete(':id') @HttpCode(204) remove(@Param('id') id: string) { return this.usersService.remove(id); } }
Service
@Injectable() export class UsersService { constructor( @InjectRepository(User) private usersRepository: Repository<User>, ) {}
findAll(): Promise<User[]> { return this.usersRepository.find(); }
findOne(id: string): Promise<User | null> { return this.usersRepository.findOneBy({ id }); } }
Key Decorators
Decorator Purpose
@Module
Define module
@Controller
Define controller
@Injectable
Mark as provider
@Get/@Post/@Put/@Delete
HTTP methods
@Body/@Param/@Query
Request data
@UseGuards
Apply guards
@UsePipes
Apply pipes
Production Readiness
Security Configuration
// main.ts - Security setup import { NestFactory } from '@nestjs/core'; import { ValidationPipe } from '@nestjs/common'; import helmet from 'helmet'; import * as compression from 'compression';
async function bootstrap() { const app = await NestFactory.create(AppModule);
// Security headers app.use(helmet());
// CORS configuration app.enableCors({ origin: process.env.ALLOWED_ORIGINS?.split(',') || [], credentials: true, });
// Global validation pipe app.useGlobalPipes(new ValidationPipe({ whitelist: true, // Strip non-whitelisted properties forbidNonWhitelisted: true, // Throw on non-whitelisted transform: true, // Auto-transform payloads }));
// Response compression app.use(compression());
// Rate limiting (with @nestjs/throttler) // Configured in AppModule
await app.listen(process.env.PORT || 3000); }
// Rate limiting module import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler'; import { APP_GUARD } from '@nestjs/core';
@Module({ imports: [ ThrottlerModule.forRoot([{ ttl: 60000, // 1 minute limit: 100, // 100 requests per minute }]), ], providers: [{ provide: APP_GUARD, useClass: ThrottlerGuard, }], }) export class AppModule {}
Health Checks
// health.controller.ts import { Controller, Get } from '@nestjs/common'; import { HealthCheck, HealthCheckService, HttpHealthIndicator, TypeOrmHealthIndicator } from '@nestjs/terminus';
@Controller('health') export class HealthController { constructor( private health: HealthCheckService, private http: HttpHealthIndicator, private db: TypeOrmHealthIndicator, ) {}
@Get() @HealthCheck() check() { return this.health.check([ () => this.db.pingCheck('database'), ]); }
@Get('ready') @HealthCheck() readiness() { return this.health.check([ () => this.db.pingCheck('database'), () => this.http.pingCheck('external-api', 'https://api.example.com/health'), ]); } }
Logging
// Structured logging with Pino import { LoggerModule } from 'nestjs-pino';
@Module({ imports: [ LoggerModule.forRoot({ pinoHttp: { level: process.env.LOG_LEVEL || 'info', transport: process.env.NODE_ENV !== 'production' ? { target: 'pino-pretty' } : undefined, redact: ['req.headers.authorization', 'req.body.password'], }, }), ], }) export class AppModule {}
Monitoring Metrics
Metric Alert Threshold
Request latency p99
500ms
Error rate (5xx)
1%
Memory usage
80%
CPU usage
70%
Active connections
1000
Request queue depth
100
Exception Handling
// Global exception filter @Catch() export class AllExceptionsFilter implements ExceptionFilter { constructor(private readonly logger: Logger) {}
catch(exception: unknown, host: ArgumentsHost) { const ctx = host.switchToHttp(); const response = ctx.getResponse(); const request = ctx.getRequest();
const status = exception instanceof HttpException
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
const message = exception instanceof HttpException
? exception.getResponse()
: 'Internal server error';
this.logger.error({
statusCode: status,
path: request.url,
method: request.method,
message,
stack: exception instanceof Error ? exception.stack : undefined,
});
response.status(status).json({
statusCode: status,
message: status === 500 ? 'Internal server error' : message,
timestamp: new Date().toISOString(),
path: request.url,
});
} }
Checklist
-
Helmet security headers enabled
-
CORS properly configured
-
Rate limiting implemented
-
Input validation with class-validator
-
Global exception filter
-
Health/readiness endpoints
-
Structured logging (no console.log)
-
Secrets via environment variables
-
HTTPS in production
-
Request timeout configured
-
Graceful shutdown handling
When NOT to Use This Skill
-
Minimalist APIs: Use Express for lightweight, unopinionated APIs
-
Maximum Performance: Use Fastify for high-throughput, low-latency requirements
-
Edge Runtimes: Use Hono for Cloudflare Workers, Vercel Edge, or Deno Deploy
-
Microservices Communication: Defer to kafka-expert or rabbitmq-expert for message brokers
-
Database Operations: Use prisma-expert or sql-expert for ORM/database specifics
-
WebSocket Implementation: Use dedicated WebSocket skill (coming soon)
Anti-Patterns
Anti-Pattern Why It's Bad Correct Approach
Using console.log() for logging No structured logging, hard to query Use nestjs-pino or Winston with structured logs
Circular dependencies between modules Causes initialization failures Use forwardRef() or redesign module boundaries
Business logic in controllers Violates SRP, hard to test Move logic to services, controllers orchestrate only
Not using DTOs for validation Security risk, inconsistent data Use class-validator with DTOs for all inputs
Hardcoding config values Not portable, security risk Use @nestjs/config with env variables
Not implementing graceful shutdown Data loss, incomplete requests Handle SIGTERM , close connections properly
Mixing ORM logic with business logic Tight coupling, hard to test Use repository pattern, inject repositories
Not using guards for authorization Security vulnerabilities Implement guards for auth/authz checks
Quick Troubleshooting
Issue Likely Cause Solution
"Circular dependency detected" Module A imports B, B imports A Use forwardRef() or extract shared logic
"Cannot find module" in tests Path mapping not resolved Configure moduleNameMapper in Jest config
Guards not executing Wrong order in app setup Apply guards after pipes: app.useGlobalGuards()
DTOs not validating ValidationPipe not global Add app.useGlobalPipes(new ValidationPipe()) in main.ts
High memory usage Memory leaks in subscriptions Use takeUntil() or unsubscribe in onModuleDestroy()
Slow startup time Too many providers/imports Lazy load modules, optimize dependency tree
404 for all routes Controllers not registered Add controllers to module's controllers array
Middleware not executing Incorrect order or path Check middleware order, use forRoutes('*') for global
Reference Documentation
-
Module Structure
-
Guards & Pipes
-
Deep: Architecture