1. Quick Start
NestJS is a progressive Node.js framework for building efficient, reliable, and scalable server-side applications. Built with TypeScript and inspired by Angular's architecture, it provides an opinionated application structure based on modules, controllers, and providers with a powerful dependency injection system.
Key features:
- TypeScript-first with full JavaScript compatibility
- Modular architecture with dependency injection
- Platform-agnostic HTTP layer (Express or Fastify)
- Built-in support for WebSockets, microservices, and GraphQL
- Extensive decorator-based routing and validation
- Lifecycle hooks for application and module events
- First-class testing support with
@nestjs/testing - REPL mode for interactive debugging
Prerequisites: Node.js >= 20
npm i -g @nestjs/cli
nest new my-project
cd my-project
npm run start
[Nest] 12345 - 05/07/2026, 10:00:00 AM LOG [NestFactory] Starting Nest application...
[Nest] 12345 - 05/07/2026, 10:00:00 AM LOG [NestApplication] Nest application successfully started
Tip
Use
npm run start:devfor watch mode during development. The@nestjs/cliprovidesnest start --watchwhich automatically recompiles on file changes.
What to try next:
- Add a new controller: see Core Usage below
- Connect a database: see Common Tasks
- Add validation: see the Pipes section
- See the Admin Guide for full architecture and configuration reference
2. Installation
System Requirements
| Requirement | Version |
|---|---|
| Node.js | >= 20 |
| npm | >= 9 (ships with Node 20) |
| TypeScript | 5.x (included) |
Installation Methods
CLI (Recommended)
npm i -g @nestjs/cli
nest new my-project
Manual Setup
mkdir my-project && cd my-project
npm init -y
npm i @nestjs/core @nestjs/common @nestjs/platform-express reflect-metadata rxjs
npm i -D @nestjs/cli typescript @types/node
Fastify Platform
npm i @nestjs/core @nestjs/common @nestjs/platform-fastify reflect-metadata rxjs
Important
The
reflect-metadatapackage is required for decorator metadata reflection. It must be imported once in your application entry point (main.ts).
Core Packages
| Package | Purpose |
|---|---|
@nestjs/core |
Framework core (DI container, lifecycle, routing) |
@nestjs/common |
Decorators, pipes, guards, interceptors, exceptions |
@nestjs/platform-express |
Express HTTP adapter (default) |
@nestjs/platform-fastify |
Fastify HTTP adapter (alternative) |
@nestjs/testing |
Testing utilities |
@nestjs/microservices |
Microservice transports (TCP, Redis, NATS, MQTT, RMQ, Kafka, gRPC) |
@nestjs/websockets |
WebSocket gateway support |
@nestjs/platform-socket.io |
Socket.IO adapter |
@nestjs/platform-ws |
WS adapter |
Post-Install Verification
nest --version
11.0.0
npm run start
[Nest] 12345 - LOG [NestFactory] Starting Nest application...
[Nest] 12345 - LOG [RoutesResolver] AppController {/}: +2ms
[Nest] 12345 - LOG [RouterExplorer] Mapped {/, GET} route +1ms
[Nest] 12345 - LOG [NestApplication] Nest application successfully started +1ms
Install Troubleshooting
Error: Cannot find module 'reflect-metadata'
Cause: Missing required dependency.
Solution:
- Install
reflect-metadata:npm i reflect-metadata - Ensure it is imported at the top of
main.ts:import 'reflect-metadata';
Error: Experimental support for decorators is a feature that is subject to change
Cause: TypeScript decorator support not enabled.
Solution:
Add to tsconfig.json:
{
"compilerOptions": {
"emitDecoratorMetadata": true,
"experimentalDecorators": true
}
}
Error: nest: command not found
Cause: NestJS CLI not installed globally or not in PATH.
Solution:
npm i -g @nestjs/cli
Or use npx:
npx @nestjs/cli new my-project
3. Configuration
NestJS applications are configured programmatically through the main.ts bootstrap function and module imports. There is no central configuration file — settings are applied via method calls on the application instance and decorator metadata.
Bootstrap Configuration
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule, {
logger: ['error', 'warn', 'log'], // Control log levels
cors: true, // Enable CORS
abortOnError: false, // Don't abort on init errors
});
app.setGlobalPrefix('api'); // Prefix all routes
app.enableVersioning({ type: VersioningType.URI }); // API versioning
await app.listen(3000);
}
bootstrap();
Common Configuration Options
| Option | Type | Default | Description |
|---|---|---|---|
logger |
LogLevel[] | LoggerService | false |
All levels | Log output control |
cors |
boolean | CorsOptions |
false |
Cross-origin resource sharing |
abortOnError |
boolean |
true |
Abort on initialization errors |
bodyParser |
boolean |
true |
Enable built-in body parsing |
rawBody |
boolean |
false |
Expose raw request body |
preview |
boolean |
false |
Preview mode (no listeners) |
forceCloseConnections |
boolean |
false |
Force-close HTTP connections on shutdown |
Environment Variables
| Variable | Purpose |
|---|---|
NO_COLOR |
Disables colored log output when set |
NEST_DEBUG |
Enables debug-level DI container logging when set |
Tip
For complex configuration, use the dynamic modules pattern with
ConfigModulefrom@nestjs/config(separate package). See the Dynamic Modules section in Common Tasks for the pattern used to build configurable modules.
See the Admin Guide for the full configuration reference including all versioning types, global prefix options, and platform-specific adapter settings.
4. Core Usage
Modules
Every NestJS application has at least one module — the root AppModule. Modules organize the application into cohesive blocks of functionality.
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
@Module({
controllers: [CatsController],
providers: [CatsService],
exports: [CatsService], // Make available to importing modules
})
export class CatsModule {}
@Module() options:
| Property | Description |
|---|---|
imports |
Modules whose exported providers are needed in this module |
controllers |
Controllers that handle incoming requests |
providers |
Services, repositories, and other injectables |
exports |
Providers available to modules that import this module |
Note
Use
@Global()on a module to make its exports available everywhere without explicit imports. Use sparingly — global modules reduce encapsulation.
Controllers
Controllers handle incoming requests and return responses. Each controller method is mapped to a route via HTTP method decorators.
import {
Controller, Get, Post, Put, Delete, Patch,
Body, Param, Query, Headers, Ip,
HttpCode, Header, Redirect,
} from '@nestjs/common';
import { CreateCatDto } from './dto/create-cat.dto';
import { CatsService } from './cats.service';
@Controller('cats')
export class CatsController {
constructor(private readonly catsService: CatsService) {}
@Post()
@HttpCode(201)
create(@Body() createCatDto: CreateCatDto) {
return this.catsService.create(createCatDto);
}
@Get()
findAll(@Query('limit') limit: number) {
return this.catsService.findAll(limit);
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.catsService.findOne(id);
}
@Put(':id')
update(@Param('id') id: string, @Body() updateCatDto: any) {
return this.catsService.update(id, updateCatDto);
}
@Delete(':id')
remove(@Param('id') id: string) {
return this.catsService.remove(id);
}
}
HTTP method decorators:
| Decorator | HTTP Method |
|---|---|
@Get() |
GET |
@Post() |
POST |
@Put() |
PUT |
@Delete() |
DELETE |
@Patch() |
PATCH |
@Options() |
OPTIONS |
@Head() |
HEAD |
@All() |
All methods |
@Search() |
SEARCH |
Parameter decorators:
| Decorator | Maps to |
|---|---|
@Body(key?) |
req.body / req.body[key] |
@Query(key?) |
req.query / req.query[key] |
@Param(key?) |
req.params / req.params[key] |
@Headers(name?) |
req.headers / req.headers[name] |
@Req() |
Full request object |
@Res() |
Full response object |
@Session() |
req.session |
@Ip() |
req.ip |
@HostParam() |
req.hosts |
@RawBody() |
Raw request body buffer |
Warning
Using
@Res()puts the response in library-specific mode — NestJS will not automatically serialize the return value. You must callres.json()orres.send()manually, or use@Res({ passthrough: true })to let NestJS handle the response while still accessing the response object.
Providers (Services)
Providers are classes annotated with @Injectable() that can be injected through the constructor. They encapsulate business logic.
import { Injectable } from '@nestjs/common';
@Injectable()
export class CatsService {
private readonly cats: Cat[] = [];
create(cat: CreateCatDto): Cat {
this.cats.push(cat);
return cat;
}
findAll(limit?: number): Cat[] {
return limit ? this.cats.slice(0, limit) : this.cats;
}
findOne(id: string): Cat {
return this.cats.find(cat => cat.id === id);
}
}
Custom providers allow more control over instantiation:
@Module({
providers: [
{ provide: CatsService, useClass: CatsService }, // Standard
{ provide: 'CONFIG', useValue: { debug: true } }, // Value
{ provide: 'ASYNC_CONFIG', useFactory: async () => { // Factory
const config = await loadConfig();
return config;
},
inject: [ConfigService], // Dependencies for factory
},
{ provide: 'ALIAS', useExisting: CatsService }, // Alias
],
})
Middleware
Middleware functions execute before route handlers. They have access to the request and response objects and the next() function.
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
console.log(`${req.method} ${req.url}`);
next();
}
}
Apply middleware in the module:
import { Module, MiddlewareConsumer, NestModule } from '@nestjs/common';
@Module({ controllers: [CatsController] })
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes(CatsController);
}
}
Pipes
Pipes transform or validate input data. NestJS includes several built-in pipes.
Built-in pipes:
| Pipe | Purpose |
|---|---|
ValidationPipe |
Validates DTOs using class-validator |
ParseIntPipe |
Parses string to integer |
ParseFloatPipe |
Parses string to float |
ParseBoolPipe |
Parses string to boolean |
ParseArrayPipe |
Parses string to array |
ParseUUIDPipe |
Validates and parses UUID strings |
ParseEnumPipe |
Validates enum values |
DefaultValuePipe |
Provides a default value when parameter is undefined |
ParseFilePipe |
Validates file uploads |
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {
return this.catsService.findOne(id);
}
Tip
Apply
ValidationPipeglobally for automatic DTO validation across all routes:app.useGlobalPipes(new ValidationPipe({ whitelist: true, // Strip non-decorated properties transform: true, // Auto-transform payloads to DTO instances forbidNonWhitelisted: true, // Throw on extra properties }));
Guards
Guards determine whether a request should be handled by the route handler. They implement the CanActivate interface.
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const request = context.switchToHttp().getRequest();
return !!request.headers.authorization;
}
}
Apply with @UseGuards():
@Controller('cats')
@UseGuards(AuthGuard)
export class CatsController {}
Or globally:
app.useGlobalGuards(new AuthGuard());
Interceptors
Interceptors add extra logic before/after method execution, transform results, handle errors, or extend behavior using RxJS observables.
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@Injectable()
export class TransformInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle().pipe(
map(data => ({ data, timestamp: new Date().toISOString() })),
);
}
}
Apply with @UseInterceptors():
@UseInterceptors(TransformInterceptor)
@Get()
findAll() {
return this.catsService.findAll();
}
Exception Filters
Exception filters catch unhandled exceptions and format error responses.
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const status = exception.getStatus();
response.status(status).json({
statusCode: status,
message: exception.message,
timestamp: new Date().toISOString(),
});
}
}
Built-in HTTP exceptions:
| Exception | Status Code |
|---|---|
BadRequestException |
400 |
UnauthorizedException |
401 |
ForbiddenException |
403 |
NotFoundException |
404 |
MethodNotAllowedException |
405 |
NotAcceptableException |
406 |
RequestTimeoutException |
408 |
ConflictException |
409 |
GoneException |
410 |
PayloadTooLargeException |
413 |
UnsupportedMediaTypeException |
415 |
UnprocessableEntityException |
422 |
InternalServerErrorException |
500 |
NotImplementedException |
501 |
BadGatewayException |
502 |
ServiceUnavailableException |
503 |
GatewayTimeoutException |
504 |
ImATeapotException |
418 |
PreconditionFailedException |
412 |
HttpVersionNotSupportedException |
505 |
throw new NotFoundException(`Cat #${id} not found`);
Execution Order
NestJS processes requests through the following pipeline:
Request → Middleware → Guards → Interceptors (before) → Pipes → Route Handler
→ Interceptors (after) → Exception Filters (on error) → Response
Important
The execution order matters for debugging. Guards run after middleware but before pipes. If a guard rejects the request, pipes and the route handler never execute.
5. Common Tasks
Set Up a Database with TypeORM
Goal: Connect a NestJS application to a SQL database using TypeORM.
Prerequisites: @nestjs/typeorm, typeorm, database driver (e.g., mysql2, pg)
Steps:
-
Install dependencies:
npm i @nestjs/typeorm typeorm mysql2 -
Configure in
AppModule:import { TypeOrmModule } from '@nestjs/typeorm'; @Module({ imports: [ TypeOrmModule.forRoot({ type: 'mysql', host: 'localhost', port: 3306, username: 'root', password: 'root', database: 'test', entities: [Photo], synchronize: true, // Disable in production }), TypeOrmModule.forFeature([Photo]), ], }) export class AppModule {} -
Create an entity:
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'; @Entity() export class Photo { @PrimaryGeneratedColumn() id: number; @Column() name: string; } -
Inject the repository:
@Injectable() export class PhotoService { constructor( @InjectRepository(Photo) private readonly photoRepo: Repository<Photo>, ) {} }
Warning
Never use
synchronize: truein production — it can cause data loss. Use TypeORM migrations instead.
Set Up a Database with Mongoose
Goal: Connect to MongoDB using Mongoose.
Prerequisites: @nestjs/mongoose, mongoose
npm i @nestjs/mongoose mongoose
import { MongooseModule } from '@nestjs/mongoose';
@Module({
imports: [
MongooseModule.forRoot('mongodb://localhost/test'),
MongooseModule.forFeature([{ name: Cat.name, schema: CatSchema }]),
],
})
export class AppModule {}
JWT Authentication
Goal: Add JWT-based authentication.
Prerequisites: @nestjs/passport, passport, passport-jwt, @nestjs/jwt
-
Create an auth module:
@Module({ imports: [ JwtModule.register({ secret: 'your-secret-key', signOptions: { expiresIn: '1h' }, }), ], providers: [AuthService, JwtStrategy], exports: [AuthService], }) export class AuthModule {} -
Create a JWT strategy:
@Injectable() export class JwtStrategy extends PassportStrategy(Strategy) { constructor() { super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), secretOrKey: 'your-secret-key', }); } async validate(payload: any) { return { userId: payload.sub, username: payload.username }; } } -
Protect routes with
@UseGuards(AuthGuard('jwt')).
File Upload
Goal: Handle file uploads using multer.
import { FileInterceptor, FilesInterceptor } from '@nestjs/platform-express';
import { UploadedFile, UploadedFiles } from '@nestjs/common';
@Controller('upload')
export class UploadController {
@Post('single')
@UseInterceptors(FileInterceptor('file'))
uploadFile(@UploadedFile() file: Express.Multer.File) {
return { filename: file.originalname, size: file.size };
}
@Post('multiple')
@UseInterceptors(FilesInterceptor('files', 10))
uploadFiles(@UploadedFiles() files: Express.Multer.File[]) {
return files.map(f => ({ filename: f.originalname }));
}
}
Server-Sent Events (SSE)
Goal: Stream real-time updates to clients.
import { Controller, Sse } from '@nestjs/common';
import { Observable, interval, map } from 'rxjs';
@Controller()
export class SseController {
@Sse('events')
events(): Observable<MessageEvent> {
return interval(1000).pipe(
map(() => ({ data: { timestamp: Date.now() } } as MessageEvent)),
);
}
}
Serve Static Files
Goal: Serve a frontend SPA alongside the API.
npm i @nestjs/serve-static
import { ServeStaticModule } from '@nestjs/serve-static';
import { join } from 'path';
@Module({
imports: [
ServeStaticModule.forRoot({
rootPath: join(__dirname, '..', 'client'),
}),
],
})
export class AppModule {}
WebSocket Gateway
Goal: Add real-time bidirectional communication.
npm i @nestjs/websockets @nestjs/platform-socket.io socket.io
import {
WebSocketGateway,
SubscribeMessage,
MessageBody,
WebSocketServer,
ConnectedSocket,
} from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';
@WebSocketGateway({ cors: true })
export class EventsGateway {
@WebSocketServer()
server: Server;
@SubscribeMessage('message')
handleMessage(
@MessageBody() data: string,
@ConnectedSocket() client: Socket,
): string {
this.server.emit('broadcast', data);
return 'Message received';
}
}
Microservices (TCP Transport)
Goal: Create a microservice communicating over TCP.
// main.ts (microservice)
import { NestFactory } from '@nestjs/core';
import { Transport, MicroserviceOptions } from '@nestjs/microservices';
async function bootstrap() {
const app = await NestFactory.createMicroservice<MicroserviceOptions>(
AppModule,
{ transport: Transport.TCP, options: { host: '0.0.0.0', port: 3001 } },
);
await app.listen();
}
bootstrap();
// Controller handling messages
@Controller()
export class MathController {
@MessagePattern({ cmd: 'sum' })
sum(data: number[]): number {
return data.reduce((a, b) => a + b, 0);
}
@EventPattern('user_created')
handleUserCreated(data: any) {
// Handle event (fire-and-forget)
}
}
Dynamic Modules
Goal: Create a reusable, configurable module.
import { DynamicModule, Module } from '@nestjs/common';
@Module({})
export class ConfigModule {
static forRoot(options: ConfigOptions): DynamicModule {
return {
module: ConfigModule,
providers: [
{ provide: 'CONFIG_OPTIONS', useValue: options },
ConfigService,
],
exports: [ConfigService],
global: true,
};
}
}
Tip
For complex dynamic modules, use
ConfigurableModuleBuilderfrom@nestjs/commonto auto-generateforRoot()andforRootAsync()static methods with less boilerplate.
MVC with Templates
Goal: Render server-side views using a template engine.
// main.ts
const app = await NestFactory.create(AppModule);
app.setBaseViewsDir(join(__dirname, '..', 'views'));
app.setViewEngine('hbs');
@Controller()
export class AppController {
@Get()
@Render('index')
root() {
return { message: 'Hello World' };
}
}
Using StreamableFile for File Downloads
import { StreamableFile } from '@nestjs/common';
import { createReadStream } from 'fs';
import { join } from 'path';
@Controller('files')
export class FilesController {
@Get('download')
getFile(): StreamableFile {
const file = createReadStream(join(process.cwd(), 'package.json'));
return new StreamableFile(file);
}
}
API Versioning
import { VersioningType } from '@nestjs/common';
const app = await NestFactory.create(AppModule);
app.enableVersioning({ type: VersioningType.URI });
@Controller({ path: 'cats', version: '1' })
export class CatsV1Controller { /* ... */ }
@Controller({ path: 'cats', version: '2' })
export class CatsV2Controller { /* ... */ }
Versioning types:
| Type | Example URL |
|---|---|
VersioningType.URI |
/v1/cats |
VersioningType.HEADER |
Header: X-API-Version: 1 |
VersioningType.MEDIA_TYPE |
Accept: application/json;v=1 |
VersioningType.CUSTOM |
Custom extraction logic |
Using Swagger/OpenAPI
npm i @nestjs/swagger
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
const config = new DocumentBuilder()
.setTitle('Cats API')
.setDescription('The cats API description')
.setVersion('1.0')
.addBearerAuth()
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api-docs', app, document);
GraphQL (Code-First)
npm i @nestjs/graphql @nestjs/apollo @apollo/server graphql
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
import { GraphQLModule } from '@nestjs/graphql';
@Module({
imports: [
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
autoSchemaFile: join(process.cwd(), 'schema.gql'),
}),
],
})
export class AppModule {}
Class Serialization
Control what properties are exposed in responses:
import { ClassSerializerInterceptor, UseInterceptors } from '@nestjs/common';
import { Exclude, Expose } from 'class-transformer';
export class UserEntity {
id: number;
name: string;
@Exclude()
password: string;
constructor(partial: Partial<UserEntity>) {
Object.assign(this, partial);
}
}
@Controller('users')
@UseInterceptors(ClassSerializerInterceptor)
export class UsersController {
@Get(':id')
findOne(@Param('id') id: string): UserEntity {
return new UserEntity({ id: 1, name: 'John', password: 'secret' });
}
}
6. Troubleshooting
Error: Nest can't resolve dependencies of the X
Cause: A provider's dependency is not available in the current module scope. The DI container cannot find a provider for the required injection token.
Solution:
- Ensure the dependency is listed in the
providersarray of the module - If the dependency lives in another module, import that module and ensure it exports the provider
- For circular dependencies, use
forwardRef():@Inject(forwardRef(() => CatsService))
Prevention: Keep module boundaries clear. Export only what other modules need.
Error: Unknown element or Unknown module
Cause: A module or provider is imported but not properly decorated or not a valid NestJS module.
Solution:
- Verify the class has the
@Module()decorator - Check for typos in import paths
- Ensure circular module dependencies use
forwardRef()
Error: Cannot GET /path
Cause: No route handler matches the requested path.
Solution:
- Check the controller has
@Controller('prefix')with the correct prefix - Verify the route decorator (
@Get(),@Post(), etc.) path matches - If using global prefix, account for it:
app.setGlobalPrefix('api')means routes become/api/... - Check if versioning is enabled — routes may require version prefix
Application hangs on shutdown
Cause: Open connections or long-running tasks preventing graceful shutdown.
Solution:
- Enable shutdown hooks:
app.enableShutdownHooks(); - Implement
OnApplicationShutdownin providers that hold connections:@Injectable() export class DbService implements OnApplicationShutdown { onApplicationShutdown(signal?: string) { return this.connection.close(); } }
Prevention: Always clean up resources in lifecycle hooks.
Error: Circular dependency between modules
Cause: Module A imports Module B, which imports Module A.
Solution:
Use forwardRef() in both module imports:
@Module({
imports: [forwardRef(() => ModuleB)],
})
export class ModuleA {}
Warning
Circular dependencies are a design smell. Refactor to extract shared functionality into a third module when possible.
Validation errors not thrown
Cause: ValidationPipe not applied or class-validator decorators missing.
Solution:
- Install required packages:
npm i class-validator class-transformer - Apply
ValidationPipeglobally or at the controller/method level - Add validation decorators to DTO classes:
import { IsString, IsInt, Min } from 'class-validator'; export class CreateCatDto { @IsString() name: string; @IsInt() @Min(0) age: number; }
WebSocket connection refused
Cause: WebSocket gateway not properly registered or CORS blocking the connection.
Solution:
- Ensure the gateway class is registered in a module's
providers - Enable CORS on the gateway:
@WebSocketGateway({ cors: { origin: '*' } })
Fastify middleware not working (nestjs/nest#11802)
Cause: Fastify middleware doesn't apply correctly when matched against routes registered via a Fastify plugin.
Solution: Register middleware before Fastify plugins, or use NestJS-level middleware (NestMiddleware) instead of Fastify-native middleware for routes managed by NestJS controllers.
7. FAQ
Q: How do I switch from Express to Fastify?
Replace @nestjs/platform-express with @nestjs/platform-fastify and update main.ts:
import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify';
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter(),
);
await app.listen(3000, '0.0.0.0'); // Fastify listens on localhost only by default
Q: What are injection scopes and when should I use them?
NestJS supports three scopes: DEFAULT (singleton), REQUEST (per-request instance), and TRANSIENT (new instance per injection). Most providers should use DEFAULT. Use REQUEST when you need per-request state (e.g., multi-tenant context). Use TRANSIENT when each consumer needs its own instance.
@Injectable({ scope: Scope.REQUEST })
export class RequestScopedService {}
Q: How do I create a hybrid application (HTTP + microservice)?
const app = await NestFactory.create(AppModule);
app.connectMicroservice<MicroserviceOptions>({
transport: Transport.TCP,
options: { port: 3001 },
});
await app.startAllMicroservices();
await app.listen(3000);
Q: How do I use the REPL mode?
import { repl } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
await repl(AppModule);
}
bootstrap();
This starts an interactive shell where you can call methods on any provider, inspect the DI container, and debug the application.
Q: How do I set up hot module replacement (HMR)?
Use the webpack-hmr.config.js with @nestjs/cli or, for ESM projects, configure HMR in main.ts:
declare const module: any;
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
if (module.hot) {
module.hot.accept();
module.hot.dispose(() => app.close());
}
}
bootstrap();
Q: What lifecycle hooks are available?
Hooks execute in this order during startup and shutdown:
| Hook | When |
|---|---|
onModuleInit |
After the module's providers are instantiated |
onApplicationBootstrap |
After all modules are initialized |
onModuleDestroy |
When close() is called (reverse order) |
beforeApplicationShutdown |
After onModuleDestroy, before connections close |
onApplicationShutdown |
After all connections are closed |
Q: How do I handle CORS?
const app = await NestFactory.create(AppModule);
app.enableCors({
origin: ['https://example.com'],
methods: 'GET,POST',
credentials: true,
});
Q: Can I use NestJS with Babel instead of TypeScript?
Yes. NestJS provides a Babel example (sample/09-babel-example). Use @babel/plugin-proposal-decorators and @babel/plugin-proposal-class-properties for decorator support.
Q: How do I test NestJS applications?
Use @nestjs/testing to create a testing module:
import { Test, TestingModule } from '@nestjs/testing';
describe('CatsController', () => {
let controller: CatsController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [CatsController],
providers: [CatsService],
}).compile();
controller = module.get<CatsController>(CatsController);
});
it('should return all cats', () => {
expect(controller.findAll()).toBeDefined();
});
});
Q: How do I use gRPC with NestJS?
const app = await NestFactory.createMicroservice<MicroserviceOptions>(
AppModule,
{
transport: Transport.GRPC,
options: {
package: 'hero',
protoPath: join(__dirname, 'hero/hero.proto'),
},
},
);
Define handlers with @GrpcMethod():
@Controller()
export class HeroController {
@GrpcMethod('HeroService', 'FindOne')
findOne(data: { id: number }): Hero {
return { id: data.id, name: 'Hero' };
}
}