1. Architecture Overview
NestJS is a TypeScript-first framework for building server-side Node.js applications. It provides a modular, decorator-driven architecture inspired by Angular, centered around dependency injection, a request processing pipeline, and platform-agnostic HTTP layer abstractions. The framework is published as a monorepo of packages under the @nestjs scope, each addressing a specific concern — from HTTP handling to microservices, WebSockets, and testing.
The core design philosophy separates the framework into three layers: the platform layer (HTTP adapters for Express or Fastify), the core layer (DI container, lifecycle management, routing, middleware pipeline), and the common layer (decorators, interfaces, pipes, exceptions, and utilities shared across all parts of the framework).
Architecture Diagram
┌─────────────────────────────────────────┐
│ NestFactory │
│ create() / createMicroservice() │
└─────────────┬───────────────────────────┘
│
┌─────────────▼───────────────────────────┐
│ NestApplication │
│ ┌──────────────────────────────────┐ │
│ │ ApplicationConfig │ │
│ │ (global prefix, versioning, │ │
│ │ pipes, guards, interceptors, │ │
│ │ filters, WebSocket adapter) │ │
│ └──────────────────────────────────┘ │
│ ┌──────────────────────────────────┐ │
│ │ NestContainer │ │
│ │ ┌────────┐ ┌────────┐ │ │
│ │ │Module A│ │Module B│ ... │ │
│ │ │ provs │ │ provs │ │ │
│ │ │ ctrls │ │ ctrls │ │ │
│ │ └────────┘ └────────┘ │ │
│ └──────────────────────────────────┘ │
│ ┌──────────────────────────────────┐ │
│ │ Request Pipeline │ │
│ │ Middleware → Guards → Intercept │ │
│ │ → Pipes → Handler → Intercept │ │
│ │ → Exception Filters │ │
│ └──────────────────────────────────┘ │
└─────────────┬───────────────────────────┘
│
┌─────────────▼───────────────────────────┐
│ Platform Adapter (HTTP) │
│ ExpressAdapter │ FastifyAdapter │
└──────────────────────────────────────────┘
Component Inventory
| Component | Package | Role |
|---|---|---|
NestFactory |
@nestjs/core |
Bootstrap entry point; creates application instances |
NestApplication |
@nestjs/core |
HTTP application with routing, middleware, lifecycle |
NestApplicationContext |
@nestjs/core |
Standalone context (no HTTP) for workers, scripts, REPL |
NestContainer |
@nestjs/core |
DI container holding all module instances and providers |
Injector |
@nestjs/core |
Resolves and instantiates providers within the container |
RoutesResolver |
@nestjs/core |
Maps controller decorators to HTTP routes |
MiddlewareModule |
@nestjs/core |
Registers and executes middleware chains |
ApplicationConfig |
@nestjs/core |
Global configuration state (prefix, pipes, guards, etc.) |
MetadataScanner |
@nestjs/core |
Scans class prototypes for decorated methods |
DiscoveryService |
@nestjs/core |
Programmatic access to all registered providers/controllers |
Reflector |
@nestjs/core |
Read metadata set by decorators on classes/methods |
LazyModuleLoader |
@nestjs/core |
Load modules on demand at runtime |
SerializedGraph / GraphInspector |
@nestjs/core |
Inspect the dependency graph for debugging |
ExpressAdapter |
@nestjs/platform-express |
Wraps Express for NestJS |
FastifyAdapter |
@nestjs/platform-fastify |
Wraps Fastify for NestJS |
IoAdapter |
@nestjs/platform-socket.io |
Socket.IO WebSocket adapter |
WsAdapter |
@nestjs/platform-ws |
ws library WebSocket adapter |
ClientsModule |
@nestjs/microservices |
Registers microservice client proxies |
Server (abstract) |
@nestjs/microservices |
Base class for all transport servers |
ClientProxy (abstract) |
@nestjs/microservices |
Base class for all transport clients |
Test |
@nestjs/testing |
Creates testing module instances |
Technology Stack
| Technology | Role | Version |
|---|---|---|
| TypeScript | Primary language | 5.x |
| Node.js | Runtime | >= 20 |
| Express | Default HTTP adapter | 5.x |
| Fastify | Alternative HTTP adapter | 5.x |
| RxJS | Reactive programming / interceptor pipeline | 7.x |
reflect-metadata |
Decorator metadata reflection | 0.2.x |
iterare |
Lazy iteration utilities | 1.2.x |
path-to-regexp |
Route pattern matching | 8.x |
uid |
Unique ID generation (mixin naming) | 2.x |
tslib |
TypeScript helper library | 2.x |
Data Flow: HTTP Request
- HTTP request arrives at the platform adapter (Express/Fastify)
- Global middleware executes in registration order
- Module-scoped middleware executes for matching routes
- Guards evaluate (
canActivate) — reject returns 403 Forbidden - Pre-handler interceptors execute (
interceptbeforenext.handle()) - Pipes transform/validate parameters
- Route handler executes
- Post-handler interceptors process the response Observable
- Exception filters catch any thrown errors, format the response
- Response is sent to the client
2. System Requirements
| Requirement | Minimum | Recommended |
|---|---|---|
| Node.js | 20.x | Latest LTS |
| npm | 9.x | Latest |
| TypeScript | 5.0+ | 5.9+ |
| OS | Linux, macOS, Windows | Any |
| Memory | 256 MB | 512 MB+ |
TypeScript Configuration Requirements
{
"compilerOptions": {
"module": "commonjs",
"target": "ES2021",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"strictNullChecks": true,
"declaration": true,
"sourceMap": true,
"outDir": "./dist"
}
}
Important
emitDecoratorMetadataandexperimentalDecoratorsare mandatory. Without them, dependency injection based on constructor parameter types will not work.
ESM Support
NestJS supports ECMAScript Modules (ESM) from Node.js 22+. For ESM projects, set "type": "module" in package.json and use the load-esm package (bundled dependency) for dynamic imports of ESM-only packages. Sample applications 34-using-esm-packages and 35-use-esm-package-after-node22 demonstrate the pattern.
3. Configuration Reference
NestFactory.create() Options
| Option | Type | Default | Description |
|---|---|---|---|
logger |
LogLevel[] | LoggerService | false |
All levels | Controls logging output |
abortOnError |
boolean |
true |
Throw during bootstrap on init errors |
snapshot |
boolean |
false |
Enable dependency graph snapshotting |
preview |
boolean |
false |
Preview mode — no HTTP listener |
forceCloseConnections |
boolean |
false |
Force-close keep-alive connections on shutdown |
cors |
boolean | CorsOptions |
false |
Enable CORS |
bodyParser |
boolean |
true |
Enable built-in body parsing |
rawBody |
boolean |
false |
Attach raw body buffer to request |
httpsOptions |
HttpsOptions |
undefined |
TLS/SSL configuration |
Application Instance Methods
| Method | Description |
|---|---|
app.listen(port, host?) |
Start HTTP server |
app.getUrl() |
Get the bound URL (call after listen()) |
app.setGlobalPrefix(prefix, options?) |
Set a global route prefix |
app.enableVersioning(options) |
Enable API versioning |
app.useGlobalPipes(...pipes) |
Register global validation/transform pipes |
app.useGlobalGuards(...guards) |
Register global authorization guards |
app.useGlobalInterceptors(...interceptors) |
Register global interceptors |
app.useGlobalFilters(...filters) |
Register global exception filters |
app.useWebSocketAdapter(adapter) |
Set WebSocket adapter |
app.connectMicroservice(options) |
Attach a microservice transport |
app.startAllMicroservices() |
Start all connected microservices |
app.enableShutdownHooks(signals?) |
Listen for OS shutdown signals |
app.enableCors(options?) |
Enable CORS with options |
app.useLogger(logger) |
Replace the default logger |
app.flushLogs() |
Flush buffered log messages |
app.select(module) |
Navigate to a specific module context |
app.get(token, options?) |
Retrieve a provider instance |
app.resolve(token, contextId?, options?) |
Resolve a request/transient-scoped provider |
app.init() |
Initialize without starting the listener |
app.close() |
Gracefully shut down |
app.useBodyParser(type, options?) |
Configure body parser (Express adapter) |
app.setBaseViewsDir(path) |
Set MVC views directory |
app.setViewEngine(engine) |
Set MVC template engine |
Environment Variables
| Variable | Effect |
|---|---|
NO_COLOR |
Disables ANSI color codes in logger output |
NEST_DEBUG |
Enables verbose DI container debug logging (shows injection resolution) |
Note
NEST_DEBUGis extremely verbose and intended for debugging circular dependency or missing provider issues. Do not enable in production.
Global Prefix Options
app.setGlobalPrefix('api', {
exclude: [
{ path: 'health', method: RequestMethod.GET },
{ path: '/', method: RequestMethod.GET },
],
});
Versioning Options
| Property | Type | Description |
|---|---|---|
type |
VersioningType |
URI, HEADER, MEDIA_TYPE, or CUSTOM |
defaultVersion |
string | string[] |
Default version for unversioned controllers |
header |
string |
Header name (for HEADER type) |
key |
string |
Media type key (for MEDIA_TYPE type) |
extractor |
Function |
Custom version extractor (for CUSTOM type) |
Module-Level Registration via APP_* Tokens
Instead of app.useGlobal*(), register enhancers within the DI system using special tokens:
import { APP_GUARD, APP_PIPE, APP_INTERCEPTOR, APP_FILTER } from '@nestjs/core';
@Module({
providers: [
{ provide: APP_GUARD, useClass: AuthGuard },
{ provide: APP_PIPE, useClass: ValidationPipe },
{ provide: APP_INTERCEPTOR, useClass: LoggingInterceptor },
{ provide: APP_FILTER, useClass: AllExceptionsFilter },
],
})
export class AppModule {}
Tip
Using
APP_*tokens allows enhancers to participate in dependency injection (they can inject other providers).app.useGlobal*()registers instances outside the DI container.
4. Deployment & Scaling
Production Build
npm run build # Compile TypeScript → dist/
node dist/main.js # Run production build
For monorepo workspaces:
npx tsc -b -v packages # Build all packages
Fastify Adapter (Production)
Fastify provides significantly higher throughput than Express. Switch by replacing the platform package:
import { NestFactory } from '@nestjs/core';
import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify';
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter({ logger: true }),
);
await app.listen(3000, '0.0.0.0');
Warning
Fastify binds to
localhostby default. In containerized environments, you must bind to0.0.0.0or the container's network interface.
Docker Deployment
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
EXPOSE 3000
CMD ["node", "dist/main.js"]
Webpack Bundling
NestJS supports Webpack for bundling applications into a single file:
nest build --webpack
Sample 08-webpack demonstrates this pattern with HotModuleReplacementPlugin for development.
Graceful Shutdown
const app = await NestFactory.create(AppModule);
app.enableShutdownHooks();
// Or with specific signals and options:
app.enableShutdownHooks([ShutdownSignal.SIGTERM, ShutdownSignal.SIGINT], {
useProcessExit: true, // Required for async loggers (e.g., Pino with transports)
});
Shutdown signal lifecycle:
- Signal received →
onModuleDestroy()(reverse module order) beforeApplicationShutdown(signal)(reverse module order)- Server connections closed
onApplicationShutdown(signal)(reverse module order)- Process exits
Hybrid Applications (HTTP + Microservices)
const app = await NestFactory.create(AppModule);
// Attach a TCP microservice
app.connectMicroservice<MicroserviceOptions>({
transport: Transport.TCP,
options: { host: '0.0.0.0', port: 3001 },
});
// Attach a Redis microservice
app.connectMicroservice<MicroserviceOptions>({
transport: Transport.REDIS,
options: { host: 'localhost', port: 6379 },
});
await app.startAllMicroservices();
await app.listen(3000);
5. Operations
Logging
NestJS ships with a built-in Logger and ConsoleLogger service.
import { Logger } from '@nestjs/common';
@Injectable()
export class CatsService {
private readonly logger = new Logger(CatsService.name);
findAll() {
this.logger.log('Fetching all cats');
this.logger.warn('This is a warning');
this.logger.error('Something failed', stackTrace);
this.logger.debug('Debug info');
this.logger.verbose('Verbose info');
}
}
Log levels (in order): verbose, debug, log, warn, error, fatal
Configure at bootstrap:
const app = await NestFactory.create(AppModule, {
logger: ['error', 'warn', 'log'],
});
Tip
Replace the built-in logger with a production logger (Winston, Pino, etc.) by implementing
LoggerServiceand callingapp.useLogger(myLogger).
Note
The
ConsoleLoggerclass can be extended for custom formatting while retaining NestJS's log level filtering logic. UnlikeLogger(which is a static wrapper),ConsoleLoggeris instantiable and supports dependency injection.
Health Checks
Use @nestjs/terminus (separate package) for health check endpoints:
npm i @nestjs/terminus
@Controller('health')
export class HealthController {
constructor(
private health: HealthCheckService,
private db: TypeOrmHealthIndicator,
) {}
@Get()
check() {
return this.health.check([
() => this.db.pingCheck('database'),
]);
}
}
Monitoring and Metrics
The DiscoveryService and GraphInspector from @nestjs/core provide introspection capabilities:
import { DiscoveryService } from '@nestjs/core';
@Injectable()
export class MetricsService {
constructor(private readonly discovery: DiscoveryService) {}
getProviderCount(): number {
return this.discovery.getProviders().length;
}
getControllerCount(): number {
return this.discovery.getControllers().length;
}
}
Note
A community feature request for a native metrics module exists (nestjs/nest#967) but is not yet implemented. Use third-party packages like
@willsoto/nestjs-prometheusor custom interceptors for request metrics.
REPL Mode
Start an interactive REPL for debugging and exploration:
// repl.ts
import { repl } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const replServer = await repl(AppModule);
replServer.setupHistory('.nestjs_repl_history', (err) => {
if (err) console.error(err);
});
}
bootstrap();
npx ts-node repl.ts
Built-in REPL functions:
| Function | Description |
|---|---|
get(token) |
Retrieve a provider instance |
resolve(token) |
Resolve a request/transient-scoped provider |
select(module) |
Navigate to a specific module |
debug() |
Print all registered modules, controllers, providers |
methods(token) |
List available methods on a provider |
$(token) |
Shorthand for get(token) |
6. Performance
Express vs. Fastify
The repository includes a benchmark suite (tools/benchmarks/) that compares Express and Fastify adapters:
| Framework | Relative Performance |
|---|---|
| Fastify (raw) | Baseline |
| NestJS + Fastify | ~95% of raw Fastify |
| Express (raw) | ~60-70% of Fastify |
| NestJS + Express | ~55-65% of Fastify |
Tip
For high-throughput APIs, use
@nestjs/platform-fastify. The Fastify adapter supportsfast-json-stringifyfor schema-based response serialization, which can further improve throughput for JSON-heavy APIs.
Injection Scopes and Performance
| Scope | Instantiation | Performance Impact |
|---|---|---|
DEFAULT (singleton) |
Once at startup | None — cached instance |
REQUEST |
Per HTTP request | Moderate — creates new instance tree per request |
TRANSIENT |
Per injection | High — creates new instance per consumer per request |
Warning
REQUESTscope propagates up the dependency chain. If a singleton depends on a request-scoped provider, the singleton must also become request-scoped. Design the dependency graph carefully to avoid unintended scope bubbling.
Lazy Module Loading
Load modules on demand to reduce startup time:
@Injectable()
export class AppService {
constructor(private readonly loader: LazyModuleLoader) {}
async loadReporting() {
const { ReportingModule } = await import('./reporting/reporting.module');
const moduleRef = await this.loader.load(() => ReportingModule);
const service = moduleRef.get(ReportingService);
return service.generateReport();
}
}
7. Security
CORS Configuration
app.enableCors({
origin: ['https://example.com', 'https://admin.example.com'],
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
credentials: true,
allowedHeaders: 'Content-Type,Authorization',
exposedHeaders: 'X-Total-Count',
maxAge: 3600,
});
Helmet Integration
npm i helmet
import helmet from 'helmet';
const app = await NestFactory.create(AppModule);
app.use(helmet());
Rate Limiting
npm i @nestjs/throttler
@Module({
imports: [
ThrottlerModule.forRoot({ ttl: 60, limit: 10 }),
],
})
export class AppModule {}
Authentication Patterns
NestJS integrates with Passport.js for authentication. The sample 19-auth-jwt demonstrates JWT authentication with:
- JwtStrategy extending
PassportStrategy(Strategy)— validates JWT tokens - AuthService — handles login logic and token generation
- JwtAuthGuard extending
AuthGuard('jwt')— protects routes - Public decorator — marks routes as public (skips authentication)
// Public decorator pattern from sample 19
export const IS_PUBLIC_KEY = 'isPublic';
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
Exception Handling Security
The built-in exception layer prevents stack traces from leaking to clients in production. HttpException responses include only the status code and message. Enable detailed errors only in development:
// Avoid in production
GraphQLModule.forRoot({
includeStacktraceInErrorResponses: false, // Default: false
});
Caution
Never expose internal error details, stack traces, or database error messages to clients. Always catch and transform internal exceptions into appropriate HTTP exceptions with safe messages.
File Upload Validation
Use ParseFilePipe with built-in validators:
@Post('upload')
@UseInterceptors(FileInterceptor('file'))
uploadFile(
@UploadedFile(
new ParseFilePipe({
validators: [
new MaxFileSizeValidator({ maxSize: 10 * 1024 * 1024 }), // 10 MB
new FileTypeValidator({ fileType: 'image/jpeg' }),
],
}),
)
file: Express.Multer.File,
) {
return { filename: file.originalname };
}
8. Developer Internals
Repository Structure
nestjs/nest/
├── packages/ # Framework source (published to npm)
│ ├── common/ # Shared decorators, interfaces, pipes, exceptions
│ │ ├── decorators/ # @Controller, @Injectable, @Module, @Inject, etc.
│ │ │ ├── core/ # Core decorators (DI, lifecycle, metadata)
│ │ │ ├── http/ # HTTP method + param decorators
│ │ │ └── modules/ # @Module, @Global
│ │ ├── enums/ # HttpStatus, RequestMethod, Scope, VersioningType
│ │ ├── exceptions/ # All HTTP exception classes
│ │ ├── file-stream/ # StreamableFile
│ │ ├── interfaces/ # TypeScript interfaces for the entire framework
│ │ ├── module-utils/ # ConfigurableModuleBuilder
│ │ ├── pipes/ # Built-in pipes (Validation, Parse*, File)
│ │ ├── serializer/ # ClassSerializerInterceptor
│ │ ├── services/ # Logger, ConsoleLogger
│ │ └── utils/ # Shared utilities
│ ├── core/ # Framework engine
│ │ ├── adapters/ # AbstractHttpAdapter
│ │ ├── discovery/ # DiscoveryService, DiscoveryModule
│ │ ├── errors/ # Framework-level error classes
│ │ ├── exceptions/ # BaseExceptionFilter, ExceptionsHandler
│ │ ├── guards/ # GuardsConsumer, GuardsContextCreator
│ │ ├── helpers/ # ContextIdFactory, ExecutionContext utilities
│ │ ├── hooks/ # Lifecycle hook callers
│ │ ├── injector/ # DI container, Injector, Module, InstanceWrapper
│ │ ├── inspector/ # GraphInspector, SerializedGraph
│ │ ├── interceptors/ # InterceptorsConsumer, InterceptorsContextCreator
│ │ ├── middleware/ # MiddlewareModule, MiddlewareContainer
│ │ ├── pipes/ # PipesConsumer, PipesContextCreator
│ │ ├── repl/ # REPL mode implementation
│ │ ├── router/ # RoutesResolver, RouterExplorer, RouterModule
│ │ └── services/ # Reflector
│ ├── microservices/ # Microservice transports
│ │ ├── client/ # ClientProxy implementations per transport
│ │ ├── ctx-host/ # Transport-specific context classes
│ │ ├── decorators/ # @MessagePattern, @EventPattern, @Client, etc.
│ │ ├── deserializers/ # Message deserializers
│ │ ├── enums/ # Transport enum
│ │ ├── events/ # Event/message pattern handling
│ │ ├── exceptions/ # RpcException, microservice exception filters
│ │ ├── helpers/ # Kafka/NATS/MQTT helpers
│ │ ├── module/ # ClientsModule
│ │ ├── record-builders/ # NatsRecord, RmqRecord, MqttRecord, KafkaRecord
│ │ ├── serializers/ # Message serializers
│ │ ├── server/ # Server implementations per transport
│ │ └── utils/ # Shared microservice utilities
│ ├── platform-express/ # Express adapter
│ │ ├── adapters/ # ExpressAdapter
│ │ └── multer/ # File upload interceptors
│ ├── platform-fastify/ # Fastify adapter
│ │ ├── adapters/ # FastifyAdapter
│ │ └── decorators/ # @InjectRawBody, Fastify-specific decorators
│ ├── platform-socket.io/ # Socket.IO WebSocket adapter
│ ├── platform-ws/ # ws WebSocket adapter
│ ├── testing/ # Test utilities (Test.createTestingModule)
│ └── websockets/ # WebSocket gateway system
│ ├── adapters/ # AbstractWsAdapter
│ ├── decorators/ # @WebSocketGateway, @SubscribeMessage, etc.
│ ├── errors/ # WS error classes
│ ├── exceptions/ # WsException, WsExceptionFilter
│ └── utils/ # Gateway metadata accessor
├── integration/ # Integration test suites
│ ├── cors/ # CORS integration tests
│ ├── hello-world/ # Basic HTTP integration
│ ├── hooks/ # Lifecycle hooks tests
│ ├── injector/ # DI system tests (circular deps, scopes)
│ ├── inspector/ # Graph inspector tests
│ ├── lazy-modules/ # Lazy loading tests
│ ├── microservices/ # All transport integration tests
│ ├── mongoose/ # Mongoose integration
│ ├── nest-application/ # Application-level tests (SSE, body parser, etc.)
│ ├── repl/ # REPL integration tests
│ ├── scopes/ # Injection scope tests
│ ├── send-files/ # StreamableFile tests
│ ├── typeorm/ # TypeORM integration
│ ├── versioning/ # API versioning tests
│ └── websockets/ # WebSocket integration tests
├── sample/ # 36 example applications
├── tools/ # Build and benchmark tools
│ ├── benchmarks/ # Express vs Fastify performance comparison
│ └── gulp/ # Build pipeline tasks
├── lerna.json # Monorepo config (version: 11.1.19)
└── package.json # Root dependencies and scripts
Architecture Deep Dive
Dependency Injection System
The DI system is the foundation of NestJS. It operates through these core classes:
NestContainer — The top-level container holding a Map<string, Module> of all registered modules. Each module is identified by a token generated from its class and dynamic metadata.
Module (internal) — Represents a single @Module() class. Contains three collections:
_providers— Map of provider wrappers (InstanceWrapper)_controllers— Map of controller wrappers_imports— Set of imported module references
InstanceWrapper — Wraps a provider/controller instance with metadata: scope, injection token, resolved value, and per-context instance maps for REQUEST/TRANSIENT scopes.
Injector — The resolution engine. For each provider, it:
- Reads constructor parameter types via
reflect-metadata - Resolves each parameter's injection token
- Looks up the matching
InstanceWrapperin the module tree - Recursively instantiates dependencies
- Caches singleton instances; creates new instances for request/transient scopes
Injection scopes:
| Scope | Constant | Behavior |
|---|---|---|
| Singleton | Scope.DEFAULT |
One instance shared across all consumers (default) |
| Request | Scope.REQUEST |
New instance per HTTP request / microservice message |
| Transient | Scope.TRANSIENT |
New instance per injection point |
Durable providers (durable: true in @Injectable()) can persist across requests when used with a custom ContextIdStrategy.
Module Resolution Order
NestFactory.create()calls theDependenciesScanner- Scanner recursively traverses
importsfrom the root module - Each module is compiled (metadata extracted, token generated)
- The
InstanceLoaderinstantiates all providers in dependency order onModuleInithooks fire in topological order (leaf modules first)onApplicationBootstraphooks fire in the same order
Lifecycle Hooks
| Hook Interface | Method | When |
|---|---|---|
OnModuleInit |
onModuleInit() |
After provider instantiation, before app is ready |
OnApplicationBootstrap |
onApplicationBootstrap() |
After all modules initialized |
OnModuleDestroy |
onModuleDestroy() |
On close(), reverse module order |
BeforeApplicationShutdown |
beforeApplicationShutdown(signal?) |
After destroy, before connections close |
OnApplicationShutdown |
onApplicationShutdown(signal?) |
Final hook, after connections closed |
All hooks support async (Promise) return values. Modules are processed in topological distance order on init, and reverse order on shutdown.
Router System
The RoutesResolver scans all controllers and their methods:
- Reads
PATH_METADATAfrom@Controller()for the base path - Reads HTTP method metadata from
@Get(),@Post(), etc. - Reads parameter metadata from
@Body(),@Param(), etc. - Creates route handlers that execute the pipeline (guards → interceptors → pipes → handler)
- Registers routes with the platform adapter
Route parameter extraction uses RouteParamtypes enum values (REQUEST=0, RESPONSE=1, NEXT=2, BODY=3, QUERY=4, PARAM=5, HEADERS=6, SESSION=7, FILE=8, FILES=9, HOST=10, IP=11, RAW_BODY=12).
Platform Adapters
Both adapters extend AbstractHttpAdapter and implement a common interface:
ExpressAdapter:
- Wraps Express 5.x
- Supports multer for file uploads (
FileInterceptor,FilesInterceptor,FileFieldsInterceptor,AnyFilesInterceptor) - Body parsing enabled by default (JSON + URL-encoded)
- Versioning supports URI, HEADER, MEDIA_TYPE, and CUSTOM types
FastifyAdapter:
- Wraps Fastify 5.x
- Uses
@fastify/multipartfor file uploads - Uses
@fastify/middiefor Express-style middleware compatibility - Uses
@fastify/corsfor CORS - Uses
@fastify/staticfor static file serving - Uses
@fastify/viewfor template rendering - Supports
@fastify/formbodyfor form data parsing - Provides
@InjectRawBody()decorator for raw body access
Microservices Architecture
Transport Implementations
| Transport | Server Class | Client Class | Package |
|---|---|---|---|
| TCP | ServerTCP |
ClientTCP |
@nestjs/microservices |
| Redis | ServerRedis |
ClientRedis |
@nestjs/microservices + ioredis |
| NATS | ServerNats |
ClientNats |
@nestjs/microservices + nats |
| MQTT | ServerMqtt |
ClientMqtt |
@nestjs/microservices + mqtt |
| RabbitMQ | ServerRMQ |
ClientRMQ |
@nestjs/microservices + amqplib |
| Kafka | ServerKafka |
ClientKafka |
@nestjs/microservices + kafkajs |
| gRPC | ServerGrpc |
ClientGrpc |
@nestjs/microservices + @grpc/grpc-js |
All servers extend the abstract Server class. All clients extend ClientProxy.
Message Patterns
@MessagePattern(pattern)— Request/response (client sends, expects a reply)@EventPattern(pattern)— Event-based (fire-and-forget, no reply)@GrpcMethod(service, method)— gRPC unary/server-streaming@GrpcStreamMethod(service, method)— gRPC client-streaming/bidirectional
Context Classes
Each transport provides a specialized context:
| Transport | Context Class | Key Methods |
|---|---|---|
| TCP | TcpContext |
getSocketRef(), getPattern() |
| Redis | RedisContext |
getChannel(), getPattern() |
| NATS | NatsContext |
getSubject(), getHeaders() |
| MQTT | MqttContext |
getTopic(), getPacket() |
| RabbitMQ | RmqContext |
getMessage(), getChannelRef(), getPattern() |
| Kafka | KafkaContext |
getMessage(), getTopic(), getPartition(), getConsumer(), getProducer(), getHeartbeat() |
All extend BaseRpcContext.
Record Builders
For transport-specific message metadata:
| Builder | Purpose |
|---|---|
NatsRecordBuilder |
Attach NATS headers to messages |
RmqRecordBuilder |
Attach RabbitMQ options (persistent, expiration, headers) |
MqttRecordBuilder |
Attach MQTT options (QoS, retain, properties) |
KafkaRecordBuilder |
Attach Kafka headers, key, partition |
ClientsModule Registration
@Module({
imports: [
ClientsModule.register([
{ name: 'MATH_SERVICE', transport: Transport.TCP, options: { port: 3001 } },
]),
// Or async:
ClientsModule.registerAsync([
{
name: 'MATH_SERVICE',
useFactory: (config: ConfigService) => ({
transport: Transport.TCP,
options: { port: config.get('MATH_PORT') },
}),
inject: [ConfigService],
},
]),
],
})
export class AppModule {}
Serialization Layer
Custom serializers/deserializers transform messages between wire format and application format:
| Class | Direction |
|---|---|
IdentitySerializer |
Passthrough (default for most transports) |
KafkaRequestSerializer |
Encodes Kafka message key/value/headers |
IncomingRequestDeserializer |
Deserializes request patterns |
IncomingEventDeserializer |
Deserializes event patterns |
KafkaRequestDeserializer |
Decodes Kafka messages |
NatsRequestJSONDeserializer |
Decodes NATS JSON messages |
NatsResponseJSONDeserializer |
Decodes NATS JSON responses |
MqttRecordSerializer |
Serializes MQTT records |
NatsRecordSerializer |
Serializes NATS records |
RmqRecordSerializer |
Serializes RMQ records |
WebSocket System
The @WebSocketGateway() decorator registers a class as a WebSocket gateway. The WebSocketsController discovers all gateways, creates server instances via the configured adapter, and binds message handlers.
Decorators:
| Decorator | Purpose |
|---|---|
@WebSocketGateway(port?, options?) |
Mark class as WebSocket gateway |
@SubscribeMessage(event) |
Handle a specific message event |
@WebSocketServer() |
Inject the underlying server instance |
@ConnectedSocket() |
Inject the client socket |
@MessageBody(property?) |
Extract message body or a property |
Gateway hooks:
| Interface | Method |
|---|---|
OnGatewayInit |
afterInit(server) |
OnGatewayConnection |
handleConnection(client, ...args) |
OnGatewayDisconnect |
handleDisconnect(client) |
Testing
@nestjs/testing provides Test.createTestingModule() for creating isolated test modules:
const moduleRef = await Test.createTestingModule({
imports: [CatsModule],
providers: [
{ provide: CatsService, useValue: mockCatsService },
],
})
.overrideProvider(CatsService)
.useValue(mockCatsService)
.overrideGuard(AuthGuard)
.useValue({ canActivate: () => true })
.compile();
Override methods:
| Method | Target |
|---|---|
overrideProvider(token) |
Replace a provider |
overrideGuard(guard) |
Replace a guard |
overrideInterceptor(interceptor) |
Replace an interceptor |
overridePipe(pipe) |
Replace a pipe |
overrideFilter(filter) |
Replace an exception filter |
overrideModule(module) |
Replace an entire module |
Each returns a builder with .useClass(), .useValue(), and .useFactory().
Code Patterns and Conventions
Decorator pattern: NestJS uses TypeScript decorators extensively with reflect-metadata for storing configuration metadata on classes and methods. All framework decorators write metadata using Reflect.defineMetadata().
Observable-based pipeline: Interceptors use RxJS Observable chains. The CallHandler.handle() returns an Observable of the route handler's response, allowing operators like map, tap, catchError, and timeout to be chained.
Commit conventions: The repo uses Angular-style commits (feat, fix, chore, etc.) with scopes matching package names (common, core, microservices, express, fastify, socket.io, ws, testing, websockets).
Build system: TypeScript project references (tsc -b) compile all packages. Gulp tasks handle file copying, sample builds, and module relocation. Lerna manages versioning and publishing.
Testing: Unit tests use Mocha + Chai + Sinon. Integration tests live in integration/ with per-feature directories. Docker Compose (integration/docker-compose.yml) provides Redis, NATS, MQTT, MySQL, MongoDB, RabbitMQ, ZooKeeper, and Kafka for transport integration tests.
Key Interfaces
| Interface | Package | Purpose |
|---|---|---|
INestApplication |
@nestjs/common |
HTTP application contract |
INestApplicationContext |
@nestjs/common |
Standalone context contract |
INestMicroservice |
@nestjs/common |
Microservice contract |
CanActivate |
@nestjs/common |
Guard interface |
NestInterceptor |
@nestjs/common |
Interceptor interface |
PipeTransform |
@nestjs/common |
Pipe interface |
ExceptionFilter |
@nestjs/common |
Exception filter interface |
NestMiddleware |
@nestjs/common |
Middleware interface |
OnModuleInit |
@nestjs/common |
Lifecycle hook |
OnApplicationBootstrap |
@nestjs/common |
Lifecycle hook |
OnModuleDestroy |
@nestjs/common |
Lifecycle hook |
BeforeApplicationShutdown |
@nestjs/common |
Lifecycle hook |
OnApplicationShutdown |
@nestjs/common |
Lifecycle hook |
Type<T> |
@nestjs/common |
Class constructor type |
DynamicModule |
@nestjs/common |
Dynamic module metadata |
ModuleMetadata |
@nestjs/common |
Static module metadata |
CustomDecorator |
@nestjs/common |
Return type of SetMetadata() |
ConfigurableModuleBuilder
Generates forRoot(), forRootAsync(), MODULE_OPTIONS_TOKEN, and ConfigurableModuleAsyncOptions for dynamic modules:
import { ConfigurableModuleBuilder } from '@nestjs/common';
export interface HttpModuleOptions {
baseUrl: string;
timeout?: number;
}
export const {
ConfigurableModuleClass,
MODULE_OPTIONS_TOKEN,
OPTIONS_TYPE,
ASYNC_OPTIONS_TYPE,
} = new ConfigurableModuleBuilder<HttpModuleOptions>()
.setClassMethodName('forRoot')
.setExtras({ isGlobal: false }, (definition, extras) => ({
...definition,
global: extras.isGlobal,
}))
.build();
Reflector Service
The Reflector class (from @nestjs/core) reads metadata set by @SetMetadata():
// Define
export const Roles = (...roles: string[]) => SetMetadata('roles', roles);
// Use
@Roles('admin')
@Get()
findAll() {}
// Read in 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;
const request = context.switchToHttp().getRequest();
return roles.includes(request.user?.role);
}
}
Reflector methods:
| Method | Description |
|---|---|
get(key, target) |
Get metadata from a single target |
getAll(key, targets) |
Get metadata from multiple targets |
getAllAndMerge(key, targets) |
Merge metadata arrays from multiple targets |
getAllAndOverride(key, targets) |
Get first non-undefined metadata |
9. Integration Points
Database Integrations
NestJS provides official wrappers for database ORMs as separate packages:
| Package | ORM | Database Types |
|---|---|---|
@nestjs/typeorm |
TypeORM | MySQL, PostgreSQL, SQLite, SQL Server, Oracle |
@nestjs/mongoose |
Mongoose | MongoDB |
@nestjs/sequelize |
Sequelize | MySQL, PostgreSQL, SQLite, SQL Server |
Each provides forRoot() / forRootAsync() and forFeature() patterns for module-level configuration.
GraphQL
| Package | Purpose |
|---|---|
@nestjs/graphql |
GraphQL module with code-first and schema-first approaches |
@nestjs/apollo |
Apollo Server driver |
@nestjs/mercurius |
Mercurius (Fastify-native) driver |
Federation support: Samples 31-graphql-federation-code-first and 32-graphql-federation-schema-first demonstrate Apollo Federation with gateway + subgraph patterns.
Caching
import { CacheModule } from '@nestjs/cache-manager';
@Module({
imports: [CacheModule.register({ ttl: 5, max: 100 })],
})
export class AppModule {}
Use @UseInterceptors(CacheInterceptor) for automatic GET response caching.
Task Scheduling
npm i @nestjs/schedule
import { Cron, Interval, Timeout } from '@nestjs/schedule';
@Injectable()
export class TasksService {
@Cron('0 */5 * * * *')
handleCron() { /* every 5 minutes */ }
@Interval(10000)
handleInterval() { /* every 10 seconds */ }
@Timeout(5000)
handleTimeout() { /* once after 5 seconds */ }
}
Event Emitter
npm i @nestjs/event-emitter
import { EventEmitter2 } from '@nestjs/event-emitter';
@Injectable()
export class OrdersService {
constructor(private eventEmitter: EventEmitter2) {}
create(order: Order) {
this.eventEmitter.emit('order.created', new OrderCreatedEvent(order));
}
}
@Injectable()
export class OrderCreatedListener {
@OnEvent('order.created')
handleOrderCreatedEvent(event: OrderCreatedEvent) {
// Handle event
}
}
Queues
npm i @nestjs/bull bull
@Module({
imports: [
BullModule.forRoot({
redis: { host: 'localhost', port: 6379 },
}),
BullModule.registerQueue({ name: 'audio' }),
],
})
export class AppModule {}
Docker Compose for Integration Testing
The repository's integration/docker-compose.yml provisions all external services needed for transport tests:
| Service | Image | Port |
|---|---|---|
| Redis | redis |
6379 |
| NATS | nats |
4222, 8222 |
| MQTT | eclipse-mosquitto |
1883, 9001 |
| MySQL | mysql:9.6.0 |
3306 |
| MongoDB | mongo:latest |
27017 |
| RabbitMQ | rabbitmq:management |
5672, 15672 |
| ZooKeeper | confluentinc/cp-zookeeper:7.9.6 |
2181 |
| Kafka | confluentinc/cp-kafka:8.2.0 |
9092, 29092 |
docker-compose -f integration/docker-compose.yml up -d
npm run test:integration
docker-compose -f integration/docker-compose.yml down
Note
KafkaJS, the Kafka client used by NestJS, appears to be no longer actively maintained (nestjs/nest#13223). Monitor for potential migration to an alternative client library.
Build Scripts
| Script | Purpose |
|---|---|
npm run build |
Compile all packages (tsc -b) |
npm run build:prod |
Clean build for release |
npm run test |
Run unit tests (Mocha) |
npm run test:integration |
Run integration tests |
npm run test:cov |
Run tests with coverage |
npm run test:docker:up |
Start Docker services for integration tests |
npm run test:docker:down |
Stop Docker services |
npm run lint |
Lint packages, integration, and spec files |
npm run format |
Format with Prettier |
npm run publish |
Build and publish via Lerna |
Contributing
The repository uses:
- Commit format: Angular-style (
feat(core): add feature,fix(microservices): fix bug) - Linting: ESLint with Prettier integration
- Pre-commit: Husky + lint-staged (auto-formats
.tsand.jsonfiles) - CI: CircleCI for build/test/lint
- Code coverage: nyc + Coveralls
- Monorepo management: Lerna (version 9.x)
Known Issues
| Issue | Status | Impact |
|---|---|---|
| nestjs/nest#13223 | Open | KafkaJS maintenance concerns |
| nestjs/nest#16257 | Open | Nested transient providers not isolated in static context |
| nestjs/nest#11802 | Open | Fastify middleware route matching with plugins |
| nestjs/nest#14590 | Open | Concurrent Kafka topic consumption |
| nestjs/nest#6771 | Open | Socket.IO forwardEventNameAs option for IoAdapter |
| nestjs/nest#1953 | Open | gRPC exception filter support |
| nestjs/nest#967 | Open | Native metrics module feature request |