Tutorials Logic, IN info@tutorialslogic.com
NestJS

Top 50 NestJS Interview Questions

NestJS interview questions covering modules, controllers, providers, dependency injection, guards, pipes, interceptors, microservices, and testing.

01

What is NestJS?

NestJS is a TypeScript-first Node.js framework for building scalable server-side applications. It uses concepts from Angular, such as modules, controllers, providers, decorators, dependency injection, guards, pipes, and interceptors. Under the hood it can run on Express or Fastify. A strong interview answer should mention that NestJS adds structure and architecture on top of the lower-level Node HTTP ecosystem.

02

Why would a team choose NestJS instead of plain Express?

A team chooses NestJS when it wants an opinionated architecture, dependency injection, TypeScript patterns, testable modules, decorators, validation, guards, interceptors, and built-in support for GraphQL, microservices, WebSockets, and OpenAPI. Express is simpler and more flexible, but large teams often benefit from NestJS conventions because code organization stays predictable as the project grows.

03

What is a NestJS module?

A module is a class decorated with @Module() that groups related controllers, providers, imports, and exports. Modules define boundaries in the application. For example, a UsersModule can contain UsersController and UsersService, export UsersService, and import DatabaseModule when it needs persistence.

Example
import { Module } from '@nestjs/common';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';

@Module({
  controllers: [UsersController],
  providers: [UsersService],
  exports: [UsersService],
})
export class UsersModule {}
04

What is the root module in NestJS?

The root module, usually AppModule, is the top-level module used to bootstrap the application. It imports feature modules and global infrastructure modules such as ConfigModule, DatabaseModule, AuthModule, or LoggerModule. It should not become a place for all business logic; it is mainly the composition root.

05

What is a controller in NestJS?

A controller handles incoming HTTP requests and returns responses. Controllers map routes using decorators such as @Controller(), @Get(), @Post(), @Patch(), and @Delete(). They should focus on HTTP concerns and delegate business logic to providers or services.

Example
import { Controller, Get, Param } from '@nestjs/common';
import { UsersService } from './users.service';

@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.usersService.findOne(id);
  }
}
06

What is a provider in NestJS?

A provider is a class or value managed by the NestJS dependency injection container. Services, repositories, factories, guards, pipes, and interceptors can all be providers. Providers let NestJS create instances, resolve dependencies, and inject them where needed.

07

How does dependency injection work in NestJS?

NestJS uses constructor injection by default. When a provider is listed in a module, Nest can instantiate it and inject its dependencies. This improves testability because tests can replace real providers with mocks. It also helps separate controllers, services, repositories, and infrastructure clients.

Example
@Injectable()
export class OrdersService {
  constructor(private readonly paymentService: PaymentService) {}

  async createOrder(dto: CreateOrderDto) {
    return this.paymentService.charge(dto.paymentToken, dto.amount);
  }
}
08

What is @Injectable() used for?

@Injectable() marks a class as a provider that can participate in NestJS dependency injection. It also enables Nest metadata reflection for dependencies. Services, custom validators, guards, interceptors, and repositories commonly use @Injectable().

09

What are custom providers in NestJS?

Custom providers let you define how a dependency is created or resolved. They are useful for injecting configuration values, external clients, factories, aliases, and different implementations for the same interface-like token.

Example
export const CACHE_CLIENT = 'CACHE_CLIENT';

@Module({
  providers: [
    {
      provide: CACHE_CLIENT,
      useFactory: () => new RedisClient(process.env.REDIS_URL),
    },
  ],
  exports: [CACHE_CLIENT],
})
export class CacheModule {}
10

What are provider scopes in NestJS?

Provider scope controls instance lifetime. The default singleton scope creates one instance per application. Request scope creates one instance per request, which is useful for request-specific context but more expensive. Transient scope creates a new instance for each injection. In interviews, mention that request-scoped providers can hurt performance if used casually.

11

What is a DTO in NestJS?

A DTO, or Data Transfer Object, defines the shape of incoming or outgoing data. In NestJS, DTO classes often work with class-validator and class-transformer so incoming requests can be validated and transformed before reaching service logic.

Example
import { IsEmail, IsString, MinLength } from 'class-validator';

export class CreateUserDto {
  @IsString()
  @MinLength(2)
  name: string;

  @IsEmail()
  email: string;
}
12

What are pipes in NestJS?

Pipes transform or validate input before it reaches the route handler. Built-in pipes include ParseIntPipe, ParseBoolPipe, ParseUUIDPipe, and ValidationPipe. Custom pipes can enforce application-specific parsing or validation rules.

Example
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {
  return this.usersService.findOne(id);
}
13

How do you enable global validation in NestJS?

Enable ValidationPipe globally in main.ts so DTO validation applies consistently. Common production options include whitelist to remove unknown fields, forbidNonWhitelisted to reject unexpected fields, and transform to convert payloads into DTO class instances.

Example
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(new ValidationPipe({
    whitelist: true,
    forbidNonWhitelisted: true,
    transform: true,
  }));
  await app.listen(3000);
}
14

What are guards in NestJS?

Guards decide whether a request should continue to a route handler. They are commonly used for authentication, authorization, feature flags, tenant access, and role checks. A guard returns true to allow the request or false/throws an exception to deny it.

Example
@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest();
    return Boolean(request.user);
  }
}
15

What is the difference between middleware and guards in NestJS?

Middleware runs before route matching details are fully handled and is good for request logging, raw request mutation, cookie parsing, or attaching basic context. Guards run after middleware and before route handlers with access to ExecutionContext and route metadata, making them better for authentication and authorization decisions.

16

What are interceptors in NestJS?

Interceptors wrap route execution. They can transform responses, add logging, measure latency, map exceptions, cache results, or add cross-cutting behavior before and after handlers run. Interceptors are useful when behavior belongs around a handler rather than inside the handler.

Example
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler) {
    const started = Date.now();
    return next.handle().pipe(
      tap(() => console.log('Request took ' + (Date.now() - started) + 'ms'))
    );
  }
}
17

What are exception filters in NestJS?

Exception filters customize how thrown errors become HTTP responses. Nest has built-in handling for HttpException, but filters are useful for consistent API error shapes, mapping database errors, hiding sensitive details, and adding request IDs to error responses.

Example
@Catch(NotFoundException)
export class NotFoundFilter implements ExceptionFilter {
  catch(exception: NotFoundException, host: ArgumentsHost) {
    const response = host.switchToHttp().getResponse();
    response.status(404).json({ error: 'Resource not found' });
  }
}
18

What is the NestJS request lifecycle?

A typical HTTP request flows through middleware, guards, interceptors before the handler, pipes, controller method, service logic, interceptors after the handler, and exception filters if an error is thrown. Understanding this order helps debug why validation, auth, logging, or error formatting is not running as expected.

19

What are custom decorators in NestJS?

Custom decorators extract reusable metadata or request values. For example, a @CurrentUser() decorator can read request.user without repeating request extraction in every controller. Decorators make controllers cleaner, but they should not hide complex business logic.

Example
export const CurrentUser = createParamDecorator(
  (_data: unknown, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    return request.user;
  },
);
20

How do you build JWT authentication in NestJS?

JWT authentication is usually built with Passport, JwtStrategy, AuthGuard, and an AuthService that signs tokens after verifying credentials. The strategy validates token payloads, while guards protect routes. Production designs should handle expiration, refresh tokens, revocation, and secret rotation.

Example
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor() {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKey: process.env.JWT_SECRET,
    });
  }

  validate(payload: { sub: string; email: string }) {
    return { id: payload.sub, email: payload.email };
  }
}
21

How do roles and permissions work in NestJS?

Roles and permissions are commonly implemented with metadata decorators plus a guard that reads the metadata through Reflector. Roles are simple but can become too coarse. Permissions or policy-based authorization works better for complex domains with ownership, tenants, and resource-level rules.

Example
export const Roles = (...roles: string[]) => SetMetadata('roles', roles);

@Roles('admin')
@Delete(':id')
remove(@Param('id') id: string) {
  return this.usersService.remove(id);
}
22

What is the ConfigModule in NestJS?

ConfigModule centralizes environment-based configuration. It can load .env files, validate environment variables, and expose ConfigService to providers. A production app should validate required settings at startup so configuration errors are caught before serving traffic.

23

What are dynamic modules in NestJS?

Dynamic modules are modules that can be configured at import time. They usually expose static methods such as forRoot() or forFeature(). They are useful for reusable infrastructure modules like database, cache, messaging, storage, and SDK clients.

Example
@Module({})
export class StorageModule {
  static forRoot(bucket: string): DynamicModule {
    return {
      module: StorageModule,
      providers: [{ provide: 'BUCKET', useValue: bucket }],
      exports: ['BUCKET'],
    };
  }
}
24

What is the difference between forRoot() and forFeature()?

forRoot() usually configures a module once at application level, such as database connection settings. forFeature() usually registers feature-specific providers, such as repositories or models needed by one module. This pattern is common in TypeORM, Mongoose, GraphQL, and custom infrastructure modules.

25

How do you integrate Prisma with NestJS?

A common pattern is to create a PrismaService that extends PrismaClient and is registered as a provider. Services inject PrismaService and use it for database access. In production, handle connection lifecycle, logging, migrations, transaction strategy, and testing with isolated databases or mocks.

Example
@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
  async onModuleInit() {
    await this.$connect();
  }
}

@Injectable()
export class UsersService {
  constructor(private readonly prisma: PrismaService) {}

  findAll() {
    return this.prisma.user.findMany();
  }
}
26

How do you integrate TypeORM with NestJS?

TypeORM integration usually uses TypeOrmModule.forRoot() for database connection configuration and TypeOrmModule.forFeature() for repositories used by feature modules. Services inject repositories with @InjectRepository(). Important interview points include migrations, connection pooling, transactions, and avoiding entity leakage into API contracts.

27

How should transactions be handled in NestJS?

Transactions should be handled close to the service method that owns the business operation. Avoid spreading one transaction across unrelated layers. With Prisma, use prisma.$transaction. With TypeORM, use a query runner or transaction manager. Tests should cover rollback behavior for partial failures.

28

How do you build GraphQL APIs in NestJS?

NestJS supports GraphQL through code-first or schema-first approaches. Code-first uses decorators on resolver classes and DTO/object types. Resolvers are similar to controllers but map GraphQL queries and mutations instead of HTTP routes. Watch for N+1 query issues and use DataLoader when needed.

Example
@Resolver(() => User)
export class UsersResolver {
  constructor(private readonly usersService: UsersService) {}

  @Query(() => [User])
  users() {
    return this.usersService.findAll();
  }
}
29

What are microservices in NestJS?

NestJS microservices let services communicate over transports such as TCP, Redis, NATS, Kafka, RabbitMQ, or gRPC. They use message patterns instead of HTTP route decorators. Microservices are useful when teams need independent scaling or async workflows, but they add operational complexity, tracing needs, and failure modes.

30

What is @MessagePattern() in NestJS?

@MessagePattern() subscribes a handler to a message pattern in a NestJS microservice. It is similar to a route decorator, but for transport messages. The handler should validate payloads, be idempotent when appropriate, and return clear responses or emit events depending on the pattern.

Example
@Controller()
export class OrdersMessageController {
  @MessagePattern('order.created')
  handleOrderCreated(data: CreateOrderMessage) {
    return this.ordersService.processCreatedOrder(data);
  }
}
31

How do WebSockets work in NestJS?

NestJS supports WebSockets through gateways. A gateway handles socket connections, subscriptions, and emitted events. Use guards or middleware-like logic for socket authentication, and plan for scaling with adapters such as Redis when multiple application instances need to broadcast events.

Example
@WebSocketGateway()
export class ChatGateway {
  @SubscribeMessage('message')
  handleMessage(@MessageBody() body: string) {
    return { event: 'message', data: body };
  }
}
32

What is CQRS in NestJS?

CQRS separates commands that change state from queries that read state. NestJS provides @nestjs/cqrs for commands, handlers, events, and sagas. It can make complex domains clearer, but it is overkill for simple CRUD applications. Use it when business workflows, events, or write/read models justify the complexity.

33

How do event emitters fit into NestJS?

Event emitters are useful for decoupling side effects from core operations. For example, after user registration, the app can emit a user.created event and listeners can send welcome emails or analytics. Be careful with reliability: in-memory events are not a durable message queue.

34

How do scheduled tasks work in NestJS?

Scheduled tasks use ScheduleModule and decorators such as @Cron(), @Interval(), and @Timeout(). They are useful for cleanup jobs, reminders, syncs, and reports. In multi-instance deployments, prevent duplicate execution with leader election, queues, or distributed locks.

Example
@Injectable()
export class ReportsJob {
  @Cron('0 2 * * *')
  generateDailyReport() {
    return this.reportsService.generateDailyReport();
  }
}
35

How do you test a NestJS service?

Use TestingModule to create the service with mocked dependencies. Unit tests should focus on business behavior and edge cases without starting an HTTP server or connecting to real external services.

Example
const moduleRef = await Test.createTestingModule({
  providers: [
    UsersService,
    { provide: PrismaService, useValue: prismaMock },
  ],
}).compile();

const service = moduleRef.get(UsersService);
36

How do you write e2e tests in NestJS?

E2E tests boot a Nest application and send real HTTP requests, often with Supertest. They should verify routing, validation, guards, serialization, and error formatting together. Use test databases or isolated containers for flows that touch persistence.

Example
const app = moduleFixture.createNestApplication();
await app.init();

await request(app.getHttpServer())
  .get('/health')
  .expect(200)
  .expect({ status: 'ok' });
37

How do you generate OpenAPI documentation in NestJS?

Use @nestjs/swagger decorators and SwaggerModule. DTOs, controllers, response decorators, and auth decorators can generate an OpenAPI document. Keep docs close to the code, but verify generated schemas because runtime validation and documentation can drift if decorators are incomplete.

Example
const config = new DocumentBuilder()
  .setTitle('Users API')
  .setVersion('1.0')
  .addBearerAuth()
  .build();

const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('docs', app, document);
38

What is serialization in NestJS?

Serialization controls what data is returned to clients. ClassSerializerInterceptor and class-transformer can hide fields such as passwords or internal IDs. Do not rely only on serialization for security; services should avoid returning sensitive data when possible.

39

How do you handle file uploads in NestJS?

NestJS can use Multer interceptors for multipart uploads. Validate file size and type, avoid trusting original filenames, store files outside the application instance when needed, and return safe metadata rather than raw file system paths.

Example
@Post('avatar')
@UseInterceptors(FileInterceptor('avatar'))
uploadAvatar(@UploadedFile() file: Express.Multer.File) {
  return { filename: file.originalname, size: file.size };
}
40

How do you handle CORS in NestJS?

Enable CORS in main.ts with a restricted origin list, allowed methods, and credentials only when required. Avoid wildcard origins for authenticated browser APIs. CORS is a browser security policy, so server-to-server requests are not blocked by it.

Example
app.enableCors({
  origin: ['https://example.com', 'https://admin.example.com'],
  credentials: true,
});
41

How do you add logging in NestJS?

NestJS includes a Logger class and also supports custom loggers. Production systems often use structured logging with request IDs, user or tenant context when safe, status codes, latency, and error causes. Interceptors are a good place for request timing logs.

42

How do you implement health checks in NestJS?

Use @nestjs/terminus or a simple controller endpoint. Liveness checks show the process is running, while readiness checks verify dependencies such as the database, cache, or queue. Keep health checks fast because orchestrators call them frequently.

43

How do you implement API versioning in NestJS?

NestJS supports URI, header, media type, and custom versioning strategies. URI versioning such as /v1/users is easy for clients and gateways to understand. Use versioning for breaking contract changes, not every small internal refactor.

Example
app.enableVersioning({
  type: VersioningType.URI,
});

@Controller({ path: 'users', version: '1' })
export class UsersV1Controller {}
44

How does caching work in NestJS?

NestJS can cache responses or service results through CacheModule, CacheInterceptor, and external stores such as Redis. Cache only data that is safe to reuse, define invalidation rules, and avoid caching personalized responses unless the cache key includes user or tenant context.

Example
@UseInterceptors(CacheInterceptor)
@Get('products')
findProducts() {
  return this.productsService.findAll();
}
45

How do you improve NestJS performance?

Improve performance by avoiding request-scoped providers unless needed, optimizing database queries, using Fastify for high-throughput cases, enabling caching carefully, avoiding heavy synchronous work, monitoring p95 and p99 latency, and keeping validation/serialization overhead reasonable for hot endpoints.

46

What is the difference between Express and Fastify adapters in NestJS?

NestJS can run on Express or Fastify through platform adapters. Express has broad middleware compatibility and familiarity. Fastify can offer better performance and schema-driven features. The choice affects middleware, plugins, request objects, and some third-party integrations.

47

How do module boundaries affect large NestJS applications?

Good module boundaries keep feature ownership clear and reduce accidental coupling. A feature module should expose only what other modules need through exports. Avoid importing everything into every module. In larger systems, shared modules should contain stable utilities, not random business logic.

48

What are common NestJS anti-patterns?

Common anti-patterns include putting business logic in controllers, making every provider request-scoped, creating circular dependencies, using global modules for everything, skipping DTO validation, leaking database entities directly as API responses, hiding too much logic in decorators, and writing tests that depend on real external services without isolation.

49

How do you handle circular dependencies in NestJS?

Circular dependencies happen when two providers or modules depend on each other. forwardRef() can break the immediate dependency cycle, but it should not be the first design choice. Often the better fix is extracting shared logic into a third service or redefining module boundaries.

Example
@Module({
  imports: [forwardRef(() => OrdersModule)],
})
export class PaymentsModule {}
50

How would you build a simple CRUD controller in NestJS?

A good CRUD controller uses route decorators, DTO validation, a service layer, and correct status codes. The controller should not contain database details. The example below shows the controller shape; UsersService owns the business and persistence logic.

Example
@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Post()
  create(@Body() dto: CreateUserDto) {
    return this.usersService.create(dto);
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.usersService.findOne(id);
  }
}

Ready to Level Up Your Skills?

Explore 500+ free tutorials across 20+ languages and frameworks.