Skip to content
NestJS interview 4 min read

Core Concepts Questions

NestJS interviews almost always start with the fundamentals: how the framework is structured, what each building block does, and why Nest sits on top of Express (or Fastify) rather than replacing it. These questions test whether you understand Nest as an opinionated, modular framework built around dependency injection — not just a router. The answers below are written the way you would explain them out loud, then backed with the exact code an interviewer expects to see.

What is NestJS and how does it differ from Express?

Express is a minimal, unopinionated HTTP library: you wire up routes, middleware, and structure entirely yourself. NestJS is a framework that uses Express (or Fastify) as its underlying HTTP engine but adds an opinionated architecture on top — modules, dependency injection, decorators, and a clear request lifecycle. The key talking point is that Nest gives you structure and testability out of the box, while Express gives you freedom and leaves organization to you.

AspectExpressNestJS
ArchitectureUnopinionated, ad hocModular, enforced by @Module
Dependency managementManual require/instantiationBuilt-in IoC / DI container
LanguageJS-firstTypeScript-first
HTTP layerItselfAdapter over Express or Fastify
TestingRoll your ownFirst-class DI-based mocking

A strong answer notes that Nest does not hide Express — you can still reach the raw request/response objects and reuse any Express middleware. Nest is a layer of organization, not a reinvention of the HTTP stack.

What are the core building blocks of a Nest application?

Interviewers want the three primary concepts named clearly: modules, controllers, and providers. Modules organize the app into cohesive features; controllers handle incoming requests and shape responses; providers (typically services) hold the business logic and are injected wherever needed. Everything is glued together by the IoC container, which Nest bootstraps at startup.

import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

@Module({
  controllers: [CatsController],
  providers: [CatsService],
})
export class CatsModule {}

What is a module and why is the AppModule special?

A module is a class annotated with @Module() that groups related controllers and providers into a single feature unit. Every Nest app has at least one — the root module (AppModule) — which Nest uses as the starting point to build the application graph: the internal map of modules and their relationships used to resolve dependencies. Feature modules are then imported into the root (directly or transitively), keeping the codebase organized by domain.

The decorator metadata has four properties worth knowing by name:

PropertyPurpose
controllersControllers instantiated by this module
providersProviders managed by Nest’s injector here
importsOther modules whose exported providers are needed
exportsProviders this module makes available to importers

A common follow-up: providers are encapsulated to their module by default. To share a service, the owning module must list it in exports, and the consuming module must imports that module.

What is a controller’s responsibility?

A controller maps an HTTP method and path to a handler. Its job is purely routing and request/response shaping — it should delegate all real work to injected services. The handler return value is serialized automatically, so you rarely touch the raw response.

import { Controller, Get, Post, Body } from '@nestjs/common';
import { CatsService } from './cats.service';
import { CreateCatDto } from './dto/create-cat.dto';

@Controller('cats')
export class CatsController {
  constructor(private readonly catsService: CatsService) {}

  @Get()
  findAll() {
    return this.catsService.findAll();
  }

  @Post()
  create(@Body() dto: CreateCatDto) {
    return this.catsService.create(dto);
  }
}

Output:

GET  /cats  ->  200 OK   [{"id":1,"name":"Whiskers"}]
POST /cats  ->  201 Created

What is a provider, and what does @Injectable do?

A provider is anything Nest can inject — most often a service, but also repositories, factories, or helpers. The @Injectable() decorator marks a class as a candidate for the IoC container so Nest can instantiate it and supply it through constructors. Without registering the provider in a module’s providers array, Nest cannot resolve it.

import { Injectable } from '@nestjs/common';
import { Cat } from './interfaces/cat.interface';

@Injectable()
export class CatsService {
  private readonly cats: Cat[] = [];

  create(cat: Cat) {
    this.cats.push(cat);
    return cat;
  }

  findAll(): Cat[] {
    return this.cats;
  }
}

A useful detail: providers are singletons by default — one instance is shared across the entire application. You can change this with provider scope (REQUEST or TRANSIENT) when per-request state is required.

How does a request flow through a Nest application?

Even at the “core concepts” level, interviewers expect a rough ordering of the request lifecycle. A request passes through middleware, then guards, then interceptors (pre), then pipes, reaches the controller handler, flows back through interceptors (post), and any thrown error is caught by exception filters.

Request
  -> Middleware
  -> Guards
  -> Interceptors (before)
  -> Pipes
  -> Controller handler (calls providers)
  -> Interceptors (after)
  -> Exception filters (on error)
Response

Best Practices

  • Organize the app into feature modules; keep AppModule thin and let it imports domain modules.
  • Keep controllers as a thin transport layer — inject services and delegate all logic to providers.
  • Mark every injectable class with @Injectable() and register it in exactly one module’s providers.
  • Export only the providers other modules genuinely need; rely on module encapsulation for the rest.
  • Prefer constructor injection over manual instantiation so dependencies stay mockable in tests.
  • Return plain values from handlers and let Nest serialize them rather than reaching for raw @Res().
Last updated June 14, 2026
Was this helpful?