NestJS Basics
10 examples to understand the NestJS 11 mental model - 7 basic and 3 intermediate.
Prerequisites
npm i -g @nestjs/cli@11
nest new my-api --strict
cd my-api
npm pkg set type=moduleFor DI patterns and cross-cutting concerns, see Dependency Injection and Guards, Interceptors & Pipes.
Basic Examples
1. Module, Controller, Service
// users.module.ts
import { Module } from "@nestjs/common";
import { UsersController } from "./users.controller.js";
import { UsersService } from "./users.service.js";
@Module({
controllers: [UsersController],
providers: [UsersService],
})
export class UsersModule {}// users.service.ts
import { Injectable } from "@nestjs/common";
@Injectable()
export class UsersService {
findAll() {
return [{ id: 1, name: "Ada Lovelace" }];
}
}// users.controller.ts
import { Controller, Get } from "@nestjs/common";
import { UsersService } from "./users.service.js";
@Controller("users")
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Get()
findAll() {
return this.usersService.findAll();
}
}@Modulegroups related controllers and providers@Injectablemarks a class for DI@Controllerdefines route prefix; methods define HTTP verbs
2. Route Parameters and Body
import { Controller, Get, Post, Body, Param } from "@nestjs/common";
@Controller("users")
export class UsersController {
@Get(":id")
findOne(@Param("id") id: string) {
return { id, name: "Ada" };
}
@Post()
create(@Body() body: { name: string; email: string }) {
return { id: "1", ...body };
}
}@Param("id")extracts route parameters@Body()extracts request body (parsed by underlying adapter)- Add ValidationPipe for automatic validation
3. Root Module and Bootstrap
// app.module.ts
import { Module } from "@nestjs/common";
import { UsersModule } from "./users/users.module.js";
@Module({
imports: [UsersModule],
})
export class AppModule {}// main.ts
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module.js";
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(process.env.PORT ?? 3000);
}
bootstrap();AppModuleis the root; import feature modulesNestFactory.createbootstraps the DI container- Use
NestFactory.create(AppModule, new FastifyAdapter())for performance
4. Global Prefix
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.setGlobalPrefix("api/v1");
await app.listen(3000);
}- All routes prefixed with
/api/v1 - Health checks often excluded:
setGlobalPrefix("api", { exclude: ["health"] })
5. ValidationPipe
import { ValidationPipe } from "@nestjs/common";
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true }));
await app.listen(3000);
}import { IsEmail, IsString, MinLength } from "class-validator";
export class CreateUserDto {
@IsString()
@MinLength(1)
name!: string;
@IsEmail()
email!: string;
}whitelist: truestrips unknown propertiestransform: trueconverts plain objects to DTO class instances- Requires
class-validatorandclass-transformer
6. Exception Filters
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(exception.getResponse());
}
}- Filters catch exceptions and shape error responses
@Catch(HttpException)scopes to HTTP exceptions- Register globally or per-controller
7. Environment Configuration
import { Module } from "@nestjs/common";
import { ConfigModule } from "@nestjs/config";
@Module({
imports: [ConfigModule.forRoot({ isGlobal: true })],
})
export class AppModule {}import { Injectable } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
@Injectable()
export class AppService {
constructor(private config: ConfigService) {}
getPort() {
return this.config.get<number>("PORT", 3000);
}
}@nestjs/configloads.envfilesConfigServiceinjects environment values- Validate env with Joi or Zod at startup
Intermediate Examples
8. Fastify Adapter
import { NestFactory } from "@nestjs/core";
import { FastifyAdapter, NestFastifyApplication } from "@nestjs/platform-fastify";
import { AppModule } from "./app.module.js";
async function bootstrap() {
const app = await NestFactory.create<NestFastifyApplication>(
AppModule,
new FastifyAdapter({ logger: true })
);
await app.listen({ port: 3000, host: "0.0.0.0" });
}
bootstrap();- Fastify adapter removes Express overhead
- See NestJS Performance Reality
9. Feature Module Exports
@Module({
providers: [UsersService],
exports: [UsersService],
})
export class UsersModule {}
@Module({
imports: [UsersModule],
controllers: [OrdersController],
})
export class OrdersModule {}
// OrdersController can inject UsersServiceexportsmakes providers available to importing modules- Without
exports, providers are private to the module
10. Lifecycle Hooks
import { Injectable, OnModuleInit, OnModuleDestroy } from "@nestjs/common";
@Injectable()
export class DatabaseService implements OnModuleInit, OnModuleDestroy {
async onModuleInit() {
console.log("Connecting to database...");
}
async onModuleDestroy() {
console.log("Closing database connections...");
}
}onModuleInitruns after module dependencies are resolvedonModuleDestroyruns on graceful shutdown- Use for connection pool setup and teardown
FAQs
When should I choose NestJS over Fastify?
NestJS when you need DI, modular architecture, guards/interceptors, and microservices patterns. Fastify for lean APIs. See Framework Selection Checklist.
Is NestJS 11 production-ready?
Yes. NestJS 11 supports Express 5 and Fastify 5 adapters on Node 24 LTS.
Does NestJS require TypeScript?
Strongly recommended. NestJS is decorator-driven and designed for TypeScript. JavaScript works but loses type safety.
How does NestJS compare to Spring Boot?
Similar mental model: modules, DI, decorators, guards. NestJS is the closest Node equivalent.
Can I use ESM with NestJS?
Yes with "type": "module" and .js extensions in imports. Check NestJS 11 ESM docs for current support status.
Related
- Dependency Injection - DI patterns
- Guards, Interceptors & Pipes - cross-cutting
- NestJS + Prisma/TypeORM - data layer
- Fastify Basics - underlying adapter
- NestJS Best Practices - section checklist
Stack versions: This page was written for Node.js 24.18.0 (Active LTS), npm 10+, TypeScript 5.6+, Express 5, Fastify 5, and NestJS 11.