NestJS is the dominant enterprise Node.js framework — built with TypeScript, heavily influenced by Angular, and designed for scalable server-side applications. Interviews test your understanding of its dependency injection system, decorators, module architecture, and Node.js runtime behaviour. This guide covers the 50 most common questions with concise answers and code examples.
Quick reference
| Topic | Most asked questions |
|---|---|
| Architecture | Modules, controllers, providers, DI |
| Decorators | @Module, @Controller, @Injectable, @Get/@Post |
| Pipes | Validation, transformation, built-in pipes |
| Guards | Auth, RBAC, JwtAuthGuard |
| Interceptors | Logging, response transformation, caching |
| Exception filters | HttpException, custom filters |
| Middleware | Functional vs class-based |
| Database | TypeORM, Prisma, repository pattern |
| Microservices | TCP, Redis transport, events vs messages |
| Testing | Unit tests, e2e tests, Test.createTestingModule |
Architecture & Core Concepts
1. What is NestJS and why use it?
NestJS is a progressive Node.js framework built with TypeScript that provides an opinionated application architecture. It's inspired by Angular's module system and uses decorators for metadata.
| Feature | Express | NestJS |
|---|---|---|
| Architecture | Unopinionated | Opinionated (modules/controllers/services) |
| TypeScript | Optional | First-class |
| DI system | None | Built-in IoC container |
| Decorators | None | Extensive |
| CLI | None | @nestjs/cli |
| Testing | Manual setup | Built-in Test module |
// main.ts — NestJS bootstrap
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
2. Explain the NestJS module system.
Every NestJS app has at least one root module (AppModule). Modules are classes decorated with @Module() that group related controllers, providers, and imports.
@Module({
imports: [TypeOrmModule.forFeature([User])], // other modules
controllers: [UsersController], // route handlers
providers: [UsersService, UsersRepository], // injectable classes
exports: [UsersService], // expose to other modules
})
export class UsersModule {}
| Property | Purpose |
|---|---|
imports |
Other modules whose exports this module needs |
controllers |
Route handler classes |
providers |
Services, repositories, guards, pipes — injected via DI |
exports |
Subset of providers available to importing modules |
3. What is Dependency Injection in NestJS?
NestJS uses an IoC (Inversion of Control) container. When a class has @Injectable(), NestJS manages its lifecycle and automatically injects it where needed.
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User)
private readonly usersRepo: Repository<User>,
private readonly mailerService: MailerService, // injected automatically
) {}
}
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {} // DI
}
Benefits: testability (swap real services for mocks), loose coupling, single instance per module by default (singleton scope).
4. What are the different provider scopes in NestJS?
| Scope | Behaviour | Use case |
|---|---|---|
DEFAULT (Singleton) |
One instance per module; shared across all requests | Stateless services |
REQUEST |
New instance per request; destroyed after response | Request-specific state, logger with request ID |
TRANSIENT |
New instance each time it's injected | Stateless but unshared |
@Injectable({ scope: Scope.REQUEST })
export class RequestContextService {
private requestId = Math.random();
}
5. What is the difference between @Module, @Controller, and @Injectable?
| Decorator | Applied to | Purpose |
|---|---|---|
@Module() |
Class | Declares a module — organises the application |
@Controller() |
Class | Declares a route handler — maps HTTP requests to methods |
@Injectable() |
Class | Marks a class as a provider — eligible for DI |
Controllers & Routing
6. How do you handle route parameters, query params, and request body?
@Controller('users')
export class UsersController {
@Get(':id')
findOne(
@Param('id', ParseIntPipe) id: number, // route param
@Query('include') include: string, // query param
) { ... }
@Post()
create(@Body() createUserDto: CreateUserDto) { ... } // request body
@Put(':id')
update(
@Param('id') id: string,
@Body() dto: UpdateUserDto,
) { ... }
@Delete(':id')
remove(@Param('id') id: string) { ... }
}
7. What HTTP method decorators does NestJS provide?
| Decorator | HTTP method |
|---|---|
@Get() |
GET |
@Post() |
POST |
@Put() |
PUT |
@Patch() |
PATCH |
@Delete() |
DELETE |
@Options() |
OPTIONS |
@Head() |
HEAD |
@All() |
All methods |
8. How do you return different HTTP status codes?
@Post()
@HttpCode(201)
create(@Body() dto: CreateUserDto) { ... }
// Or use @Res() for full control (not recommended — breaks interceptors)
@Get()
findAll(@Res() res: Response) {
res.status(200).json([]);
}
// Throw exceptions for errors
@Get(':id')
async findOne(@Param('id') id: string) {
const user = await this.usersService.findOne(+id);
if (!user) throw new NotFoundException(`User #${id} not found`);
return user;
}
9. How do you use route prefixes and versioning?
// Global prefix
app.setGlobalPrefix('api');
// Module-level prefix
@Controller('users') // → /api/users
// Versioning
app.enableVersioning({ type: VersioningType.URI });
@Controller({ path: 'users', version: '1' }) // → /api/v1/users
Pipes
10. What are pipes and what are they used for?
Pipes operate on the arguments being processed by a route handler. They do two things: transformation (parse string to int) and validation (throw if DTO is invalid).
// Built-in pipes
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) { ... }
// ValidationPipe with class-validator
@Post()
create(@Body(ValidationPipe) dto: CreateUserDto) { ... }
11. What built-in pipes does NestJS provide?
| Pipe | Purpose |
|---|---|
ValidationPipe |
Validates DTOs with class-validator |
ParseIntPipe |
Converts string to integer |
ParseFloatPipe |
Converts string to float |
ParseBoolPipe |
Converts "true"/"false" to boolean |
ParseArrayPipe |
Parses array from query string |
ParseUUIDPipe |
Validates UUID format |
ParseEnumPipe |
Validates enum membership |
DefaultValuePipe |
Provides a fallback default value |
12. How do you use ValidationPipe with DTOs?
// DTO with class-validator decorators
import { IsEmail, IsString, MinLength, IsOptional } from 'class-validator';
export class CreateUserDto {
@IsEmail()
email: string;
@IsString()
@MinLength(8)
password: string;
@IsString()
@IsOptional()
name?: string;
}
// Enable globally (recommended)
app.useGlobalPipes(new ValidationPipe({
whitelist: true, // strip unknown properties
forbidNonWhitelisted: true, // throw on unknown properties
transform: true, // auto-transform to DTO class instance
}));
13. How do you create a custom pipe?
import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
@Injectable()
export class PositiveIntPipe implements PipeTransform {
transform(value: any, metadata: ArgumentMetadata) {
const val = parseInt(value, 10);
if (isNaN(val) || val <= 0) {
throw new BadRequestException('Value must be a positive integer');
}
return val;
}
}
// Usage
@Get(':count')
list(@Param('count', PositiveIntPipe) count: number) { ... }
Guards
14. What are guards and how do they work?
Guards determine whether a request should be handled by the route handler. They implement CanActivate and execute after middleware but before interceptors and pipes.
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private jwtService: JwtService) {}
canActivate(context: ExecutionContext): boolean {
const request = context.switchToHttp().getRequest();
const token = request.headers.authorization?.split(' ')[1];
if (!token) throw new UnauthorizedException();
try {
request.user = this.jwtService.verify(token);
return true;
} catch {
throw new UnauthorizedException();
}
}
}
15. How do you implement role-based access control (RBAC)?
// Custom decorator
export const Roles = (...roles: string[]) => SetMetadata('roles', roles);
// Guard
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const roles = this.reflector.get<string[]>('roles', context.getHandler());
if (!roles) return true; // no roles required
const { user } = context.switchToHttp().getRequest();
return roles.some(role => user.roles?.includes(role));
}
}
// Usage
@Get('admin')
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('admin')
adminOnly() { ... }
16. How do you apply guards globally, at controller level, or at method level?
// Global
app.useGlobalGuards(new JwtAuthGuard());
// Controller level
@Controller('admin')
@UseGuards(JwtAuthGuard, RolesGuard)
export class AdminController { ... }
// Method level
@Get('secret')
@UseGuards(JwtAuthGuard)
getSecret() { ... }
Interceptors
17. What are interceptors in NestJS?
Interceptors wrap the execution of route handlers. They can:
- Transform the response
- Add extra logic before/after the handler
- Override the response entirely
- Handle errors
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const start = Date.now();
const req = context.switchToHttp().getRequest();
console.log(`→ ${req.method} ${req.url}`);
return next.handle().pipe(
tap(() => console.log(`← ${Date.now() - start}ms`)),
);
}
}
18. How do you create a response transformation interceptor?
// Wrap all responses in { data: ... }
@Injectable()
export class TransformInterceptor<T> implements NestInterceptor<T, { data: T }> {
intercept(context: ExecutionContext, next: CallHandler): Observable<{ data: T }> {
return next.handle().pipe(
map(data => ({ data })),
);
}
}
// Global registration
app.useGlobalInterceptors(new TransformInterceptor());
19. What is the execution order of NestJS request lifecycle?
Incoming Request
→ Middleware (global → module)
→ Guards (global → controller → method)
→ Interceptors (global → controller → method) — before
→ Pipes (global → controller → method → parameter)
→ Route Handler
← Interceptors (method → controller → global) — after
← Exception Filters (if error thrown)
← Response
Exception Filters
20. What built-in HTTP exceptions does NestJS provide?
| Exception | Status Code |
|---|---|
BadRequestException |
400 |
UnauthorizedException |
401 |
ForbiddenException |
403 |
NotFoundException |
404 |
ConflictException |
409 |
UnprocessableEntityException |
422 |
InternalServerErrorException |
500 |
HttpException (base) |
Any |
21. How do you create a custom exception filter?
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception.getStatus();
response.status(status).json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
message: exception.message,
});
}
}
// Apply globally
app.useGlobalFilters(new HttpExceptionFilter());
Middleware
22. How is NestJS middleware different from Express middleware?
NestJS middleware is equivalent to Express middleware — it's called before route handlers and has access to req, res, and next. However, it cannot access the execution context (unlike Guards/Interceptors).
// Functional middleware
export function logger(req: Request, res: Response, next: NextFunction) {
console.log(`${req.method} ${req.url}`);
next();
}
// Class-based middleware
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
console.log(`${req.method} ${req.url}`);
next();
}
}
// Apply in module
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes({ path: 'users', method: RequestMethod.GET });
}
}
23. When should you use middleware vs guards vs interceptors?
| Concern | Use |
|---|---|
| Request logging | Middleware or Interceptor |
| Authentication (parse token) | Middleware |
| Authorization (can user access?) | Guard |
| Response transformation | Interceptor |
| Request/response logging with timing | Interceptor |
| CORS, body parsing | Middleware |
| Caching responses | Interceptor |
| Business-rule access control | Guard |
Database Integration
24. How do you integrate TypeORM with NestJS?
// app.module.ts
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'postgres',
host: process.env.DB_HOST,
port: +process.env.DB_PORT,
username: process.env.DB_USER,
password: process.env.DB_PASS,
database: process.env.DB_NAME,
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: false, // never true in production
}),
TypeOrmModule.forFeature([User]), // per-module
],
})
export class AppModule {}
// Entity
@Entity('users')
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column({ unique: true })
email: string;
@CreateDateColumn()
createdAt: Date;
}
// Service injection
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User)
private usersRepo: Repository<User>,
) {}
findAll() { return this.usersRepo.find(); }
findOne(id: number) { return this.usersRepo.findOneBy({ id }); }
}
25. How do you use Prisma with NestJS?
// prisma.service.ts
@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
async onModuleInit() {
await this.$connect();
}
}
// Module
@Module({
providers: [PrismaService, UsersService],
exports: [PrismaService],
})
export class DatabaseModule {}
// Service
@Injectable()
export class UsersService {
constructor(private prisma: PrismaService) {}
findAll() {
return this.prisma.user.findMany();
}
create(data: Prisma.UserCreateInput) {
return this.prisma.user.create({ data });
}
}
26. What is the Repository pattern in NestJS with TypeORM?
The Repository pattern wraps the ORM to keep business logic clean. NestJS TypeORM integration provides Repository<Entity> out of the box, but you can extend it for custom queries.
@Injectable()
export class UsersRepository {
constructor(
@InjectRepository(User)
private readonly repo: Repository<User>,
) {}
async findByEmail(email: string): Promise<User | null> {
return this.repo.findOne({ where: { email } });
}
async findActiveUsers(): Promise<User[]> {
return this.repo.createQueryBuilder('user')
.where('user.isActive = :active', { active: true })
.orderBy('user.createdAt', 'DESC')
.getMany();
}
}
Authentication & Security
27. How do you implement JWT authentication in NestJS?
// auth.module.ts
@Module({
imports: [
JwtModule.register({
secret: process.env.JWT_SECRET,
signOptions: { expiresIn: '7d' },
}),
UsersModule,
],
providers: [AuthService, JwtStrategy],
controllers: [AuthController],
})
export class AuthModule {}
// jwt.strategy.ts
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: process.env.JWT_SECRET,
});
}
async validate(payload: { sub: number; email: string }) {
return { userId: payload.sub, email: payload.email };
}
}
// auth.service.ts
@Injectable()
export class AuthService {
constructor(
private usersService: UsersService,
private jwtService: JwtService,
) {}
async login(email: string, password: string) {
const user = await this.usersService.findByEmail(email);
if (!user || !bcrypt.compareSync(password, user.password)) {
throw new UnauthorizedException('Invalid credentials');
}
return {
access_token: this.jwtService.sign({ sub: user.id, email: user.email }),
};
}
}
// Protect route
@Get('profile')
@UseGuards(AuthGuard('jwt'))
getProfile(@Request() req) {
return req.user;
}
28. How do you protect routes using @nestjs/passport?
// Built-in guard
@UseGuards(AuthGuard('jwt'))
// Custom guard (recommended for reuse)
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
handleRequest(err: any, user: any) {
if (err || !user) throw err || new UnauthorizedException();
return user;
}
}
// Usage
@Get('me')
@UseGuards(JwtAuthGuard)
getMe(@Request() req) { return req.user; }
Configuration
29. How do you manage configuration/environment variables in NestJS?
// app.module.ts
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true, // no need to import in every module
envFilePath: '.env',
validationSchema: Joi.object({
PORT: Joi.number().default(3000),
DATABASE_URL: Joi.string().required(),
JWT_SECRET: Joi.string().required(),
}),
}),
],
})
export class AppModule {}
// Inject in service
@Injectable()
export class UsersService {
constructor(private config: ConfigService) {}
getDbUrl() { return this.config.get<string>('DATABASE_URL'); }
}
30. What is forRoot vs forFeature in NestJS?
| Method | Usage |
|---|---|
forRoot() |
Configure once at root — establishes the connection/global config |
forFeature() |
Register feature-level resources (entities, stores) in a specific module |
// Root — database connection
TypeOrmModule.forRoot({ ... })
// Feature — entity repositories for UsersModule
TypeOrmModule.forFeature([User, UserProfile])
Custom Decorators
31. How do you create custom parameter decorators?
// Extract current user from request
export const CurrentUser = createParamDecorator(
(data: string | undefined, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
const user = request.user;
return data ? user?.[data] : user;
},
);
// Usage
@Get('profile')
@UseGuards(JwtAuthGuard)
getProfile(@CurrentUser() user: User) { return user; }
@Get('email')
@UseGuards(JwtAuthGuard)
getEmail(@CurrentUser('email') email: string) { return email; }
32. How do you create custom class decorators?
// Compose multiple decorators
export function Auth(...roles: string[]) {
return applyDecorators(
SetMetadata('roles', roles),
UseGuards(JwtAuthGuard, RolesGuard),
ApiBearerAuth(), // Swagger
ApiUnauthorizedResponse({ description: 'Unauthorized' }),
);
}
// Usage — clean and reusable
@Get('admin')
@Auth('admin')
adminPanel() { ... }
Microservices
33. How does NestJS support microservices?
NestJS has a built-in @nestjs/microservices package with multiple transport layers.
// TCP microservice
const app = await NestFactory.createMicroservice<MicroserviceOptions>(AppModule, {
transport: Transport.TCP,
options: { port: 3001 },
});
await app.listen();
// Message handler in microservice
@MessagePattern('get_user')
getUser(@Payload() data: { id: number }) {
return this.usersService.findOne(data.id);
}
// Client (calling microservice)
@Injectable()
export class ApiGatewayService {
private client = ClientProxyFactory.create({
transport: Transport.TCP,
options: { port: 3001 },
});
getUser(id: number) {
return this.client.send('get_user', { id }).toPromise();
}
}
34. What is the difference between @MessagePattern and @EventPattern?
| Decorator | Communication | Response |
|---|---|---|
@MessagePattern |
Request-response | Returns a value to the caller |
@EventPattern |
Fire-and-forget | No return value; async notification |
// Request-response
@MessagePattern('calculate_tax')
calculate(@Payload() data: TaxData): number {
return data.amount * 0.2;
}
// Event (one-way)
@EventPattern('user.created')
handleUserCreated(@Payload() data: UserCreatedEvent) {
this.emailService.sendWelcome(data.email);
}
35. What transports does NestJS microservices support?
| Transport | Use case |
|---|---|
| TCP | Simple inter-service communication |
| Redis | Pub/sub with fan-out |
| NATS | Cloud-native messaging |
| Kafka | High-throughput event streaming |
| RabbitMQ (AMQP) | Reliable queuing |
| gRPC | High-performance typed contracts |
| MQTT | IoT/lightweight messaging |
WebSockets
36. How do you implement WebSockets in NestJS?
@WebSocketGateway({
cors: { origin: '*' },
namespace: '/chat',
})
export class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect {
@WebSocketServer() server: Server;
handleConnection(client: Socket) {
console.log(`Client connected: ${client.id}`);
}
handleDisconnect(client: Socket) {
console.log(`Client disconnected: ${client.id}`);
}
@SubscribeMessage('message')
handleMessage(client: Socket, payload: { room: string; text: string }) {
this.server.to(payload.room).emit('message', {
sender: client.id,
text: payload.text,
});
}
}
Testing
37. How do you write unit tests in NestJS?
// users.service.spec.ts
describe('UsersService', () => {
let service: UsersService;
let repo: jest.Mocked<Repository<User>>;
beforeEach(async () => {
const module = await Test.createTestingModule({
providers: [
UsersService,
{
provide: getRepositoryToken(User),
useValue: {
find: jest.fn(),
findOne: jest.fn(),
save: jest.fn(),
create: jest.fn(),
},
},
],
}).compile();
service = module.get<UsersService>(UsersService);
repo = module.get(getRepositoryToken(User));
});
it('should return all users', async () => {
const users = [{ id: 1, email: 'test@test.com' }] as User[];
repo.find.mockResolvedValue(users);
const result = await service.findAll();
expect(result).toEqual(users);
expect(repo.find).toHaveBeenCalledTimes(1);
});
});
38. How do you write e2e tests in NestJS?
// app.e2e-spec.ts
describe('UsersController (e2e)', () => {
let app: INestApplication;
beforeAll(async () => {
const module = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = module.createNestApplication();
app.useGlobalPipes(new ValidationPipe({ whitelist: true }));
await app.init();
});
afterAll(() => app.close());
it('GET /users — returns array', () => {
return request(app.getHttpServer())
.get('/users')
.expect(200)
.expect(res => expect(Array.isArray(res.body)).toBe(true));
});
it('POST /users — validates body', () => {
return request(app.getHttpServer())
.post('/users')
.send({ email: 'not-an-email' })
.expect(400);
});
});
39. How do you mock providers in NestJS tests?
// Using useValue
{ provide: UsersService, useValue: { findAll: jest.fn().mockResolvedValue([]) } }
// Using useFactory
{ provide: ConfigService, useFactory: () => ({ get: jest.fn().mockReturnValue('secret') }) }
// Using jest.spyOn after getting the real service
const service = module.get<UsersService>(UsersService);
jest.spyOn(service, 'findAll').mockResolvedValue([]);
Performance & Advanced
40. How do you enable caching in NestJS?
// Module setup
@Module({
imports: [
CacheModule.register({
store: redisStore,
host: 'localhost',
port: 6379,
ttl: 60, // seconds
}),
],
})
// Interceptor-based (automatic)
@UseInterceptors(CacheInterceptor)
@Controller('products')
export class ProductsController {
@Get()
@CacheTTL(30)
findAll() { ... }
}
// Manual cache
@Injectable()
export class ProductsService {
constructor(@Inject(CACHE_MANAGER) private cache: Cache) {}
async findFeatured() {
const cached = await this.cache.get('featured');
if (cached) return cached;
const data = await this.fetchFeatured();
await this.cache.set('featured', data, 60);
return data;
}
}
41. What is lazy module loading and when do you use it?
Lazy loading delays the instantiation of a module until it's actually needed — useful in serverless environments or when certain features are rarely used.
@Injectable()
export class LazyLoaderService {
constructor(private readonly lazyModuleLoader: LazyModuleLoader) {}
async loadHeavyModule() {
const { HeavyModule } = await import('./heavy/heavy.module');
const moduleRef = await this.lazyModuleLoader.load(() => HeavyModule);
return moduleRef.get(HeavyService);
}
}
42. How do you handle file uploads in NestJS?
// Using Multer
@Post('upload')
@UseInterceptors(FileInterceptor('file', {
storage: diskStorage({
destination: './uploads',
filename: (req, file, cb) => cb(null, `${Date.now()}-${file.originalname}`),
}),
fileFilter: (req, file, cb) => {
const allowed = ['image/jpeg', 'image/png', 'image/webp'];
cb(null, allowed.includes(file.mimetype));
},
limits: { fileSize: 5 * 1024 * 1024 }, // 5MB
}))
uploadFile(@UploadedFile() file: Express.Multer.File) {
return { filename: file.filename };
}
43. How do you implement task scheduling in NestJS?
// Install: @nestjs/schedule
@Module({ imports: [ScheduleModule.forRoot()] })
@Injectable()
export class TasksService {
// Runs every day at midnight
@Cron('0 0 * * *')
cleanupExpiredSessions() { ... }
// Runs every 30 seconds
@Interval(30_000)
healthCheck() { ... }
// Runs once after 10 seconds
@Timeout(10_000)
initialSync() { ... }
}
44. What is ExecutionContext and Reflector?
ExecutionContext provides a context-agnostic way to access request details (works for HTTP, WebSockets, and microservices). Reflector reads metadata set by SetMetadata.
// In a guard or interceptor
canActivate(context: ExecutionContext): boolean {
// HTTP
const req = context.switchToHttp().getRequest();
// WebSocket
const client = context.switchToWs().getClient();
// Microservice
const data = context.switchToRpc().getData();
// Read metadata
const roles = this.reflector.get<string[]>('roles', context.getHandler());
const classRoles = this.reflector.get<string[]>('roles', context.getClass());
// Merge both (method overrides class)
const allRoles = this.reflector.getAllAndMerge('roles', [
context.getHandler(),
context.getClass(),
]);
}
45. How do you use @nestjs/swagger to document your API?
// main.ts
const config = new DocumentBuilder()
.setTitle('Users API')
.setDescription('User management endpoints')
.setVersion('1.0')
.addBearerAuth()
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api/docs', app, document);
// DTO
export class CreateUserDto {
@ApiProperty({ example: 'user@example.com' })
@IsEmail()
email: string;
@ApiProperty({ example: 'StrongPassword1!' })
@IsString()
@MinLength(8)
password: string;
}
// Controller
@ApiTags('users')
@ApiBearerAuth()
@Controller('users')
export class UsersController {
@ApiOperation({ summary: 'Get all users' })
@ApiResponse({ status: 200, type: [UserDto] })
@Get()
findAll() { ... }
}
46. How does forRootAsync work for dynamic configuration?
Use forRootAsync when the config values come from asynchronous sources like ConfigService or secrets managers.
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
useFactory: (config: ConfigService) => ({
type: 'postgres',
url: config.get<string>('DATABASE_URL'),
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: false,
}),
inject: [ConfigService],
})
Common Mistakes
47–50. Anti-patterns to avoid
| Anti-pattern | Problem | Fix |
|---|---|---|
| Circular dependency | UsersModule ↔ AuthModule causes Nest DI to fail |
Use forwardRef(() => Module) or restructure |
Injecting Repository directly in controller |
Bypasses service layer | Always inject through service |
Using @Res() everywhere |
Breaks interceptors and exception filters | Use return values; use @Res() only when truly needed |
Skipping whitelist: true in ValidationPipe |
Allows extra properties through, potential mass-assignment | Always use whitelist: true, forbidNonWhitelisted: true |
synchronize: true in production TypeORM |
Auto-drops and recreates columns, causes data loss | Set to false, use migrations |
Not using OnModuleDestroy |
Connections leak on app shutdown | Implement OnModuleDestroy to clean up resources |
| Global state in singleton services | REQUEST-scoped side effects bleed between requests | Use Scope.REQUEST or pass context explicitly |
| Blocking event loop with sync heavy work | Freezes all requests | Offload to worker threads or queue |
NestJS vs Express vs Fastify
| Feature | NestJS | Express | Fastify |
|---|---|---|---|
| Architecture | Opinionated (modules/DI) | Unopinionated | Unopinionated |
| TypeScript | First-class | Optional | Optional |
| DI container | Built-in | None | None |
| Performance | Slightly slower (DI overhead) | Fast | Fastest |
| Learning curve | Steep | Gentle | Gentle |
| Testing | Excellent (Test module) | Manual setup | Manual setup |
| CLI | Yes (nest generate) |
No | No |
| Microservices | Built-in | None | None |
| WebSockets | Built-in (Gateways) | Manual | Manual |
| Documentation | Excellent | Good | Good |
| Use case | Enterprise/large teams | Simple APIs, full control | Performance-critical |
FAQ
Q: Should I use NestJS or Express for a new project?
Choose NestJS for enterprise applications, large teams, or projects that will grow significantly. The opinionated structure, built-in DI, and testing utilities pay off at scale. Choose Express for simple microservices, quick prototypes, or when you want full control over the stack.
Q: Does NestJS support both REST and GraphQL?
Yes. Install @nestjs/graphql and @apollo/server (or Mercurius for Fastify). NestJS provides a code-first (TypeScript decorators) and schema-first (SDL) approach.
Q: How does NestJS handle circular dependencies?
Use forwardRef:
@Module({
imports: [forwardRef(() => AuthModule)],
})
export class UsersModule {}
Or restructure to extract shared logic into a third module both can import without a cycle.
Q: Can NestJS run on Fastify instead of Express?
Yes:
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter(),
);
Fastify is ~20-30% faster but has a slightly different ecosystem (some Express middleware won't work directly).
Q: How do you handle database transactions in NestJS with TypeORM?
async createWithProfile(dto: CreateUserDto) {
return this.dataSource.transaction(async manager => {
const user = manager.create(User, dto);
await manager.save(user);
const profile = manager.create(UserProfile, { userId: user.id });
await manager.save(profile);
return user;
});
}
Q: What's the difference between useClass, useValue, and useFactory for providers?
| Option | Use when |
|---|---|
useClass |
Standard class instantiation via DI (default) |
useValue |
Provide a pre-built object or primitive (config object, mock) |
useFactory |
Async or dynamic provider creation (depends on other providers) |
useExisting |
Alias one provider to another |
{ provide: APP_GUARD, useClass: JwtAuthGuard }
{ provide: 'CONFIG', useValue: { timeout: 5000 } }
{ provide: DatabaseService, useFactory: async (config) => { ... }, inject: [ConfigService] }