codejump

Interview questionsNestJS

NestJS interview questions and answers

30 NestJS questions picked from Codejump's quizzes, from the basics a junior is expected to know to the details that trip up senior candidates. Each one comes with its answer and an explanation — try to answer before you open it.

Junior level NestJS questions

  1. 1.What does the value returned from a strategy's validate() method become?

    javascript
    import { Injectable } from '@nestjs/common';
    import { PassportStrategy } from '@nestjs/passport';
    import { Strategy } from 'passport-jwt';
    
    @Injectable()
    export class JwtStrategy extends PassportStrategy(Strategy) {
      async validate(payload: { sub: string; email: string }): Promise<{ id: string; email: string }> {
        return { id: payload.sub, email: payload.email }; // -> req.user
      }
    }
    • A.The HTTP response body
    • B.The object Passport assigns to req.user
    • C.The JWT signing secret
    • D.The next middleware
    Show answer

    Answer: B. The object Passport assigns to req.user

    Whatever validate() returns becomes request.user, available to downstream guards, interceptors, and the route handler. For JwtStrategy, validate(payload) typically returns a minimal user object derived from the token claims; for LocalStrategy, it returns the authenticated user after checking the password. Returning null/false (or throwing) signals failed authentication.

    From the quiz Passport & JWT Basics

  2. 2.By default, where does ConfigModule.forRoot() look for the .env file?

    javascript
    ConfigModule.forRoot({
      // Default looks for ./.env relative to process.cwd()
      envFilePath: '.env', // explicit, same as default
    });
    • A.The node_modules/@nestjs/config directory
    • B.The user's home directory
    • C.The directory set by process.cwd() (the project root from which you start the app)
    • D.The compiled dist/ output folder only
    Show answer

    Answer: C. The directory set by process.cwd() (the project root from which you start the app)

    By default ConfigModule resolves .env relative to the current working directory (process.cwd()), which is typically the project root where you run npm run start. You can override this with the envFilePath option. It does not read from node_modules or the home directory, and the working directory is what matters, not where the compiled files live.

    From the quiz ConfigModule Basics

  3. 3.A request comes in as GET /products?category=books&sort=asc. Which decorator reads category?

    javascript
    import { Controller, Get, Query } from '@nestjs/common';
    
    @Controller('products')
    export class ProductsController {
      @Get()
      list(@Query('category') category: string, @Query('sort') sort: string): string {
        return `category=${category} sort=${sort}`;
      }
    }
    • A.@Query('category')
    • B.@Param('category')
    • C.@Body('category')
    • D.@Req('category')
    Show answer

    Answer: A. @Query('category')

    @Query('category') extracts a single named value from the URL query string. Without an argument, @Query() returns the entire parsed query object (e.g. { category: 'books', sort: 'asc' }). @Param is for path segments, @Body for request payloads, and @Req injects the raw request object and does not accept a field name like that.

    From the quiz Routes & Decorators

  4. 4.Which decorator maps a normal entity property to a regular database column?

    javascript
    import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
    
    @Entity()
    export class Article {
      @PrimaryGeneratedColumn()
      id: number;
    
      @Column({ length: 200 })
      title: string;
    
      @Column({ type: 'text', nullable: true })
      body: string | null;
    }
    • A.@Field()
    • B.@Property()
    • C.@ColumnDef()
    • D.@Column()
    Show answer

    Answer: D. @Column()

    @Column() maps a property to a table column and accepts options such as type, length, nullable, default and unique. Without @Column() (or another column-producing decorator), TypeORM ignores the property entirely. @Field() belongs to GraphQL, and @Property()/@ColumnDef() are not TypeORM decorators.

    From the quiz TypeORM Basics

  5. 5.Which properties does the @Module() decorator metadata object accept?

    javascript
    import { Module } from '@nestjs/common';
    import { CatsController } from './cats.controller';
    import { CatsService } from './cats.service';
    
    @Module({
      imports: [],
      controllers: [CatsController],
      providers: [CatsService],
      exports: [CatsService],
    })
    export class CatsModule {}
    • A.routes, middleware, pipes, filters
    • B.dependencies, services, endpoints, globals
    • C.imports, controllers, providers, exports
    • D.components, directives, pipes, bootstrap
    Show answer

    Answer: C. imports, controllers, providers, exports

    The @Module() decorator takes a metadata object with four main properties: imports (other modules whose exports you need), controllers (request handlers), providers (injectable services), and exports (the subset of this module's providers made available to importing modules). This metadata is what Nest uses to assemble the application graph.

    From the quiz NestJS Basics

  6. 6.Which decorator handles a fire-and-forget event (no response expected)?

    javascript
    import { Controller } from '@nestjs/common';
    import { EventPattern, Payload } from '@nestjs/microservices';
    
    @Controller()
    export class UserEventsController {
      @EventPattern('user.created')
      handleUserCreated(@Payload() data: { id: string }) {
        // process asynchronously; nothing returned to producer
      }
    }
    • A.@MessagePattern()
    • B.@OnEvent()
    • C.@EventPattern()
    • D.@Post()
    Show answer

    Answer: C. @EventPattern()

    @EventPattern(pattern) handles events emitted with ClientProxy.emit(); the producer does not wait for a reply. @MessagePattern() is request-response. @OnEvent() belongs to the in-process @nestjs/event-emitter package, not microservice transports, and @Post() is HTTP. Correct answer: C

    From the quiz Transport & Patterns

  7. 7.What does consumer.apply(LoggerMiddleware).forRoutes('users') do?

    javascript
    configure(consumer: MiddlewareConsumer): void {
      consumer
        .apply(LoggerMiddleware)
        .forRoutes('users'); // only /users and its sub-paths
    }
    • A.Applies the middleware globally to every route in the app
    • B.Applies the middleware to all routes except users
    • C.Applies the middleware only to routes whose path starts with users
    • D.Registers users as a provider token
    Show answer

    Answer: C. Applies the middleware only to routes whose path starts with users

    forRoutes scopes the middleware to specific paths or controllers. Passing the string 'users' binds the middleware to routes under that path. To make it global you would use '*' (or a wildcard pattern depending on the version). To skip routes you use exclude, not forRoutes.

    From the quiz Middleware & Filters

  8. 8.What is a guard's responsibility, and what must its canActivate return?

    javascript
    import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
    
    @Injectable()
    export class AuthGuard implements CanActivate {
      canActivate(context: ExecutionContext): boolean {
        const req = context.switchToHttp().getRequest();
        return Boolean(req.headers.authorization);
      }
    }
    • A.Decide whether a request may proceed; return a boolean (or Promise/Observable of one)
    • B.Transform input; return the transformed value
    • C.Format errors; return an HTTP response
    • D.Cache responses; return cached data
    Show answer

    Answer: A. Decide whether a request may proceed; return a boolean (or Promise/Observable of one)

    Guards determine whether the current request is allowed to reach the handler, typically for authentication/authorization. The canActivate method returns true to allow and false (or throws) to deny; it may return a boolean, Promise<boolean>, or Observable<boolean>. Returning false causes Nest to throw a ForbiddenException by default. Transformation, error formatting, and caching are handled by pipes, filters, and interceptors respectively.

    From the quiz Pipes, Guards & Interceptors

  9. 9.In NestJS terminology, what is a "provider"?

    javascript
    @Module({
      providers: [
        CatsService,                          // class provider
        { provide: 'API_KEY', useValue: 'abc' }, // value provider
      ],
    })
    export class CatsModule {}
    • A.Any value (service, factory, repository, constant) that can be injected as a dependency through the DI container
    • B.Only a class decorated with @Controller()
    • C.A frontend component
    • D.An environment variable
    Show answer

    Answer: A. Any value (service, factory, repository, constant) that can be injected as a dependency through the DI container

    A provider is anything that the DI container can create and inject. While the most common provider is an @Injectable() service class, providers also include factory-produced values, plain constants (via useValue), and aliased implementations. The unifying idea is that a provider is registered under a token and supplied to dependents on request.

    From the quiz Injectable & DI Basics

  10. 10.What does jest.fn() provide when mocking a dependency's method?

    javascript
    const repo = { findById: jest.fn().mockResolvedValue({ id: '1' }) };
    await service.load('1');
    expect(repo.findById).toHaveBeenCalledWith('1');
    • A.A real implementation copied from the original method
    • B.A network stub server
    • C.A TypeScript type guard
    • D.A mock function that records calls and lets you define return values and assertions
    Show answer

    Answer: D. A mock function that records calls and lets you define return values and assertions

    jest.fn() creates a mock function that tracks how it was called (arguments, call count) and can be configured with return/resolved values, making it ideal for stubbing a collaborator's methods. It carries no real implementation unless you give it one, and it is unrelated to type guards or network servers. You then assert with matchers like toHaveBeenCalledWith.

    From the quiz Unit Testing Basics

Mid level NestJS questions

  1. 11.What is the idiomatic way to mark certain routes as not requiring authentication when a JWT guard is global?

    javascript
    import { SetMetadata } from '@nestjs/common';
    
    export const IS_PUBLIC_KEY = 'isPublic';
    export const Public = (): MethodDecorator & ClassDecorator =>
      SetMetadata(IS_PUBLIC_KEY, true);
    
    // usage
    @Public()
    @Get('health')
    health(): string {
      return 'ok';
    }
    • A.Remove the global guard entirely
    • B.Wrap the route in middleware
    • C.Add @UseFilters(PublicFilter)
    • D.Create a @Public() decorator using SetMetadata and have the guard skip routes carrying that metadata
    Show answer

    Answer: D. Create a @Public() decorator using SetMetadata and have the guard skip routes carrying that metadata

    With a globally applied JWT guard, you opt specific routes out by attaching metadata via a custom @Public() decorator built on SetMetadata. The guard reads this metadata with Reflector and returns true early for public routes, skipping token checks. This is cleaner than toggling the guard per controller and keeps the default secure-by-default posture.

    From the quiz JWT Strategies & RBAC

  2. 12.What is the purpose of ConfigModule.forFeature(myConfig)?

    javascript
    import databaseConfig from './database.config'; // registerAs('database', ...)
    
    @Module({
      imports: [ConfigModule.forFeature(databaseConfig)],
    })
    export class DatabaseModule {}
    • A.It re-initializes the global config and reloads the .env file
    • B.It marks the feature module as the only one allowed to read config
    • C.It registers a partial/namespaced config (typically a registerAs factory) scoped for injection within a specific feature module
    • D.It disables global config for that module
    Show answer

    Answer: C. It registers a partial/namespaced config (typically a registerAs factory) scoped for injection within a specific feature module

    forFeature() registers additional namespaced configuration (usually created with registerAs) so a feature module can inject its slice of config in a strongly typed way, without re-running forRoot(). It composes with the global config rather than replacing or disabling it. This keeps feature-specific config close to the feature while still benefiting from the loaded global setup.

    From the quiz Namespaced Config & Hooks

  3. 13.A handler returns a Promise<User>. What does NestJS do?

    javascript
    import { Controller, Get, Param } from '@nestjs/common';
    import { UsersService } from './users.service';
    
    @Controller('users')
    export class UsersController {
      constructor(private readonly users: UsersService) {}
    
      @Get(':id')
      async findOne(@Param('id') id: string) {
        return await this.users.findOne(id); // resolved value is serialized
      }
    }
    • A.It awaits the promise and serializes the resolved value automatically
    • B.It rejects because handlers must be synchronous
    • C.It returns the unresolved Promise object to the client
    • D.It only works if you also add @Async()
    Show answer

    Answer: A. It awaits the promise and serializes the resolved value automatically

    NestJS handlers natively support async/await and any thenable. When a handler returns a Promise (or an RxJS Observable), Nest waits for it to resolve and then serializes the resolved value as the response. There is no @Async() decorator. This is why service calls returning promises can be returned directly from controllers.

    From the quiz Advanced Routing

  4. 14.How do you load the posts relation when calling find() without making the relation eager?

    javascript
    await userRepo.find({ relations: ['posts'] });
    
    // nested object form, also supports deep relations
    await userRepo.find({
      relations: { posts: { comments: true } },
    });
    • A.find({ join: ['posts'] })
    • B.find({ with: 'posts' })
    • C.find({ include: { posts: true } })
    • D.find({ relations: ['posts'] }) (or relations: { posts: true })
    Show answer

    Answer: D. find({ relations: ['posts'] }) (or relations: { posts: true })

    The relations option in FindOptions tells TypeORM which relations to join and hydrate for that particular query, available as an array of paths or as a nested object form. include is Prisma syntax, not TypeORM, and with/join are not valid FindOptions keys for this purpose. Using relations per-query keeps relations off by default while loading them where needed.

    From the quiz Relations & QueryBuilder

  5. 15.What is the effect of marking a module with @Global()?

    javascript
    @Global()
    @Module({
      providers: [ConfigService],
      exports: [ConfigService],
    })
    export class ConfigModule {}
    • A.Its providers run in a global server worker thread
    • B.Its exported providers become available everywhere without each consumer importing the module, after it is registered once
    • C.It disables module encapsulation for the entire application
    • D.It makes the module load before main.ts
    Show answer

    Answer: B. Its exported providers become available everywhere without each consumer importing the module, after it is registered once

    @Global() registers a module's exported providers in a global scope so any other module can inject them without importing the module explicitly. The global module must still be imported once (typically in the root module) so Nest registers it. Overusing @Global() is discouraged because it hides dependencies and harms modularity.

    From the quiz Modules & Sharing

  6. 16.What do @Payload() and @Ctx() extract in a message handler?

    javascript
    @EventPattern('user.created')
    handle(@Payload() data: { id: string }, @Ctx() context: KafkaContext) {
      const topic = context.getTopic();
      // data = message body, context = transport metadata
    }
    • A.@Payload() extracts transport metadata; @Ctx() extracts the message body
    • B.Both extract the same thing
    • C.@Payload() extracts the message data/body; @Ctx() extracts the transport-specific context object (e.g. RmqContext, KafkaContext)
    • D.@Ctx() is only for HTTP
    Show answer

    Answer: C. @Payload() extracts the message data/body; @Ctx() extracts the transport-specific context object (e.g. RmqContext, KafkaContext)

    @Payload() gives you the deserialized message data sent by the producer, while @Ctx() gives the transport-specific context (channel, original message, topic/partition, etc.) used for things like manual acknowledgment. They are complementary, not identical, and @Ctx() is a microservices decorator, not HTTP. You can also pass a property key to @Payload('field') to pick a sub-field. Correct answer: C

    From the quiz Redis, RabbitMQ & More

  7. 17.When you pass an object to forRoutes, which shape does the RouteInfo use?

    javascript
    import { RequestMethod } from '@nestjs/common';
    
    consumer
      .apply(LoggerMiddleware)
      .forRoutes({ path: 'cats', method: RequestMethod.POST }); // only POST /cats
    • A.{ path: string, method: RequestMethod }
    • B.{ url: string, verb: string }
    • C.{ route: string, type: HttpMethod }
    • D.{ pattern: string, http: string }
    Show answer

    Answer: A. { path: string, method: RequestMethod }

    A RouteInfo object has a path (the route string) and a method from the RequestMethod enum (GET, POST, ALL, etc.). This lets you bind a middleware to a single HTTP verb on a path rather than every verb. The same shape is accepted by both forRoutes and exclude.

    From the quiz Custom Middleware

  8. 18.A custom guard implements CanActivate. How does it access the incoming HTTP request?

    javascript
    import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
    
    @Injectable()
    export class TokenGuard implements CanActivate {
      canActivate(context: ExecutionContext): boolean {
        const request = context.switchToHttp().getRequest();
        return typeof request.headers['x-token'] === 'string';
      }
    }
    • A.By injecting Request directly into the constructor
    • B.By reading process.domain.request
    • C.Via context.getRequest() without switching
    • D.Via context.switchToHttp().getRequest()
    Show answer

    Answer: D. Via context.switchToHttp().getRequest()

    A guard receives an ExecutionContext, which is transport-agnostic. To get the HTTP request you must narrow it with switchToHttp() and then call getRequest(). You cant inject the request into a singleton guards constructor (its per-request), and `getRequest()` doesnt exist directly on ExecutionContext without switching. This pattern keeps guards reusable across transports.

    From the quiz Custom Pipeline Elements

  9. 19.What does useExisting create?

    javascript
    @Module({
      providers: [
        LoggerService,
        { provide: 'AppLogger', useExisting: LoggerService }, // alias, same instance
      ],
    })
    export class AppModule {}
    • A.A brand-new second instance of a provider
    • B.An alias token that points to an already-registered provider, so both tokens resolve to the same single instance
    • C.A request-scoped copy of a provider
    • D.A circular dependency
    Show answer

    Answer: B. An alias token that points to an already-registered provider, so both tokens resolve to the same single instance

    useExisting defines an alias: a new token that resolves to the same instance as an existing provider. No additional instance is created. It's useful for exposing one provider under multiple tokens — for example offering a stable public token while the implementation is registered under a class.

    From the quiz Custom Providers

  10. 20.In an e2e test, how do you send HTTP requests to the Nest app with supertest?

    javascript
    import * as request from 'supertest';
    
    const res = await request(app.getHttpServer()).get('/users').expect(200);
    expect(res.body).toEqual([]);
    • A.request(app.listen())
    • B.request(app.getHttpServer()).get('/users')
    • C.request('http://localhost:3000') always
    • D.app.inject({ method: 'GET' }) only works in Nest
    Show answer

    Answer: B. request(app.getHttpServer()).get('/users')

    After app.init(), you pass app.getHttpServer() (the underlying Node HTTP server) to supertest's request(), then chain HTTP verbs and assertions without binding a real port. app.listen() returns a promise to a listening app, not a server for supertest, and hardcoding a URL requires the app to actually listen on that port. (app.inject exists for the Fastify adapter, but the supertest pattern is the common Express path.)

    From the quiz Mocking & E2E Tests

Senior level NestJS questions

  1. 21.How does a jti blacklist in Redis support refresh token revocation?

    javascript
    async revoke(jti: string, expSeconds: number): Promise<void> {
      await redis.set(`bl:${jti}`, '1', 'EX', expSeconds);
    }
    async assertNotRevoked(jti: string): Promise<void> {
      if (await redis.exists(`bl:${jti}`)) throw new UnauthorizedException('Token revoked');
    }
    • A.It stores every valid access token
    • B.On rotation/logout, the old refresh token's jti is added to a denylist with a TTL matching its expiry; future use of that jti is rejected
    • C.It encrypts the JWT
    • D.It rotates the signing secret per request
    Show answer

    Answer: B. On rotation/logout, the old refresh token's jti is added to a denylist with a TTL matching its expiry; future use of that jti is rejected

    Each refresh token carries a unique jti. When it is rotated or the user logs out, you record that jti in a Redis denylist with a TTL equal to the token's remaining lifetime, so it self-cleans. On every refresh you check the denylist first; a hit means the token was revoked or reused, so you reject it (and optionally revoke the whole token family). This adds revocation to otherwise stateless tokens at low cost.

    From the quiz Advanced Auth Patterns

  2. 22.You load envFilePath: ['.env.local', '.env'] AND the variable is also already set in the real shell environment. Which value wins?

    javascript
    // Precedence (highest first):
    // 1) existing process.env (shell/CI)
    // 2) .env.local   (first in array)
    // 3) .env         (later in array)
    • A..env always wins because it is the base file
    • B.The existing real environment variable wins; dotenv does not override variables already present in process.env
    • C..env.local wins over the shell environment
    • D.A random one of the three is chosen
    Show answer

    Answer: B. The existing real environment variable wins; dotenv does not override variables already present in process.env

    dotenv (used under the hood) never overwrites variables that already exist in process.env, so a value injected by the real shell/CI environment takes precedence over any .env file. Among the files, earlier array entries (.env.local) win over later ones (.env), but none of them override an existing process env var. This is why containerized/12-factor deployments rely on real env vars overriding committed defaults.

    From the quiz Validation & Shutdown

  3. 23.In a module's configure(consumer), how do you scope middleware to only GET /users?

    javascript
    import { MiddlewareConsumer, NestModule, RequestMethod, Module } from '@nestjs/common';
    import { LoggerMiddleware } from './logger.middleware';
    
    @Module({})
    export class UsersModule implements NestModule {
      configure(consumer: MiddlewareConsumer): void {
        consumer
          .apply(LoggerMiddleware)
          .forRoutes({ path: 'users', method: RequestMethod.GET });
      }
    }
    • A.consumer.apply(LoggerMiddleware).forRoutes('users')
    • B.consumer.apply(LoggerMiddleware).forRoutes({ path: 'users', method: RequestMethod.GET })
    • C.consumer.use(LoggerMiddleware, '/users')
    • D.@UseMiddleware(LoggerMiddleware) on the handler
    Show answer

    Answer: B. consumer.apply(LoggerMiddleware).forRoutes({ path: 'users', method: RequestMethod.GET })

    Middleware is configured in a module implementing NestModule via consumer.apply(...).forRoutes(...). Passing a route object { path, method } scopes it to a specific path and HTTP method. Passing just 'users' (option A) would apply it to all methods on that path. There is no consumer.use() signature like C, and no @UseMiddleware decorator — middleware can't be attached with a method decorator.

    From the quiz Versioning & Streaming

  4. 24.How do you configure and target a second, independently-named database connection?

    javascript
    TypeOrmModule.forRoot({ name: 'reporting', type: 'postgres', /* ... */ });
    
    @Module({ imports: [TypeOrmModule.forFeature([Stat], 'reporting')] })
    export class ReportingModule {}
    
    @Injectable()
    class StatsService {
      constructor(
        @InjectRepository(Stat, 'reporting') private stats: Repository<Stat>,
      ) {}
    }
    • A.Call forRoot() once and pass an array of databases
    • B.Multiple connections are not supported
    • C.Call forRoot({ name: 'reporting', ... }) for the second connection, then use forFeature([X], 'reporting') and @InjectRepository(X, 'reporting')
    • D.Use two AppModules
    Show answer

    Answer: C. Call forRoot({ name: 'reporting', ... }) for the second connection, then use forFeature([X], 'reporting') and @InjectRepository(X, 'reporting')

    TypeORM supports multiple connections by giving each a unique name in its forRoot() (the default connection omits name). To use a non-default connection you must pass that name to forFeature([Entity], 'reporting') and to @InjectRepository(Entity, 'reporting'), otherwise NestJS resolves the default connection's repository. A single forRoot() does not accept an array of databases, and you never duplicate AppModule.

    From the quiz Transactions & Advanced

  5. 25.What is the lifetime of a default-scoped provider declared in a statically-imported module?

    javascript
    @Injectable() // default DEFAULT scope → one instance app-wide
    export class CounterService {
      private count = 0; // shared across all consumers — beware request state
      inc(): number { return ++this.count; }
    }
    • A.One instance per HTTP request
    • B.A single instance per application (singleton), instantiated once during bootstrap and reused
    • C.A new instance every time it is injected
    • D.One instance per controller method call
    Show answer

    Answer: B. A single instance per application (singleton), instantiated once during bootstrap and reused

    Default-scoped (singleton) providers are instantiated once when Nest initializes the module graph at bootstrap, and the same instance is shared for the application's lifetime. This is why singleton providers must avoid storing per-request state. Only non-default scopes (REQUEST, TRANSIENT) change this behavior.

    From the quiz Dynamic Modules

  6. 26.What is the key difference between core NATS and NATS JetStream in a NestJS microservice?

    javascript
    // JetStream-style options enable persistence/acks
    ClientsModule.register([{
      name: 'NATS',
      transport: Transport.NATS,
      options: { servers: ['nats://localhost:4222'], jetstream: true },
    }]);
    • A.They are identical
    • B.Core NATS persists messages; JetStream does not
    • C.Core NATS is at-most-once with no persistence; JetStream adds persistence, replay, and stronger delivery guarantees (acks, durable consumers)
    • D.JetStream only supports request-response
    Show answer

    Answer: C. Core NATS is at-most-once with no persistence; JetStream adds persistence, replay, and stronger delivery guarantees (acks, durable consumers)

    Core NATS is a lightweight, in-memory pub/sub with at-most-once delivery — if no subscriber is online, the message is lost. JetStream layers a persistence and streaming engine on top, providing message storage, replay, acknowledgments, and durable consumers for at-least-once semantics. They are not identical, and JetStream supports both pub/sub and request-response patterns. Choose JetStream when you need durability and redelivery. Correct answer: C

    From the quiz gRPC & Kafka

  7. 27.A single shared filter must behave differently for HTTP and WebSocket requests. Which call tells you the current transport?

    javascript
    catch(exception: unknown, host: ArgumentsHost): void {
      if (host.getType() === 'http') {
        const res = host.switchToHttp().getResponse();
        // build HTTP response
      } else if (host.getType() === 'ws') {
        const client = host.switchToWs().getClient();
        // emit WS error
      }
    }
    • A.host.getRequest().protocol
    • B.host.switchToHttp().getType()
    • C.host.contextName
    • D.host.getType() returning 'http', 'ws', or 'rpc'
    Show answer

    Answer: D. host.getType() returning 'http', 'ws', or 'rpc'

    ArgumentsHost.getType() returns the current execution context type as 'http', 'ws', or 'rpc' (custom strings are possible for custom transports). You branch on it before narrowing with switchToHttp / switchToWs / switchToRpc, allowing one filter to support multiple transports safely.

    From the quiz Filter Hierarchy

  8. 28.A @Roles() decorator is applied at both class and method level. You want the union of all roles. Which Reflector method do you use?

    javascript
    const roles = this.reflector.getAllAndMerge<string[]>('roles', [
      context.getHandler(),
      context.getClass(),
    ]); // e.g. class ['user'] + method ['admin'] => ['admin','user']
    • A.getAllAndMerge([handler, class])
    • B.getAllAndOverride([handler, class])
    • C.get('roles', handler)
    • D.getAll('roles')
    Show answer

    Answer: A. getAllAndMerge([handler, class])

    getAllAndMerge(key, targets) collects the metadata from every target and merges them — concatenating arrays / combining objects — producing the union of class and method roles. getAllAndOverride returns only the first defined value (the more specific one), discarding the others. Use merge when you want cumulative metadata and override when the most specific should win.

    From the quiz Execution Order & Scopes

  9. 29.What problem do "durable" providers solve?

    javascript
    import { Injectable, Scope } from '@nestjs/common';
    
    @Injectable({ scope: Scope.REQUEST, durable: true })
    export class TenantConnection {}
    // Combined with a custom ContextIdStrategy keyed on tenant id,
    // instances are reused per tenant rather than per request.
    • A.They allow scoped instances to be shared across requests that belong to the same logical sub-tree (e.g., per tenant) instead of being recreated for every single request
    • B.They persist provider instances to disk
    • C.They make providers immune to garbage collection
    • D.They disable dependency injection caching
    Show answer

    Answer: A. They allow scoped instances to be shared across requests that belong to the same logical sub-tree (e.g., per tenant) instead of being recreated for every single request

    Durable providers optimize the scope-bubbling cost in multi-tenant scenarios. Using a ContextIdStrategy, you can key DI sub-trees by something stable (like a tenant ID) so the framework reuses the same scoped instances across many requests sharing that key, rather than rebuilding the request sub-tree every time. The provider is marked durable: true.

    From the quiz Scopes & Advanced DI

  10. 30.How do you retrieve a Scope.REQUEST provider from a TestingModule?

    javascript
    const service = await moduleRef.resolve(RequestScopedService);
    expect(service).toBeDefined();
    • A.moduleRef.get(Service) like any singleton
    • B.await moduleRef.resolve(Service), because request/transient-scoped providers aren't singletons and require async resolution per context
    • C.You cannot test scoped providers
    • D.moduleRef.getRequestScoped(Service)
    Show answer

    Answer: B. await moduleRef.resolve(Service), because request/transient-scoped providers aren't singletons and require async resolution per context

    get() only works for singleton (default-scoped) providers; request- and transient-scoped providers are created per DI context, so you must use the async resolve(), optionally with a context id, to obtain an instance. Each resolve() for transient scope can yield a different instance. Scoped providers are testable; there is no getRequestScoped method.

    From the quiz Advanced Testing

Practise 1150+ more NestJS questions

Timed quizzes with instant feedback, the questions you miss coming back until they stick, coding challenges and AI mock interviews — free to start.