Nest.js is a TypeScript-first backend framework built on Node.js. It uses modules, dependency injection, and decorators similar to Angular, and is well-suited for scalable REST APIs and microservices.
1. Why Nest.js?
- Structure: Modules, controllers, services, and clear separation of concerns.
- TypeScript: First-class support and strong typing.
- Dependency injection: Built-in IoC container for testability and modularity.
- Decorators:
@Controller(),@Get(),@Post(),@Body(), etc. for routing and validation. - Ecosystem: OpenAPI (Swagger), TypeORM/Prisma, Passport (auth), etc.
- Testing: Unit and e2e testing built into the CLI.
2. Prerequisites
- Node.js 18+
- npm or pnpm / yarn
3. Project Setup
npm i -g @nestjs/cli
nest new my-api
cd my-api
Options: npm/yarn/pnpm, strict TypeScript. This creates src/ with main.ts, app.module.ts, app.controller.ts, app.service.ts.
4. Project Structure (Default)
my-api/
src/
main.ts # Bootstrap app
app.module.ts # Root module
app.controller.ts # Root controller
app.service.ts # Root service
test/
package.json
tsconfig.json
5. Main Entry (Bootstrap)
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.setGlobalPrefix('api/v1'); // optional: all routes under /api/v1
app.enableCors();
await app.listen(process.env.PORT || 3000);
}
bootstrap();
6. Module, Controller, Service
Module — groups related pieces (controllers, services, imports):
// src/app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UsersModule } from './users/users.module';
@Module({
imports: [UsersModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
Controller — handles HTTP and delegates to services:
// src/users/users.controller.ts
import { Controller, Get, Post, Body, Param, ParseIntPipe } from '@nestjs/common';
import { UsersService } from './users.service';
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Get()
findAll() {
return this.usersService.findAll();
}
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {
return this.usersService.findOne(id);
}
@Post()
create(@Body() body: { email: string; name?: string }) {
return this.usersService.create(body);
}
}
Service — business logic and data access:
// src/users/users.service.ts
import { Injectable, NotFoundException } from '@nestjs/common';
@Injectable()
export class UsersService {
async findAll() {
return []; // or prisma.user.findMany()
}
async findOne(id: number) {
const user = await this.findUserById(id); // your logic
if (!user) throw new NotFoundException('User not found');
return user;
}
async create(data: { email: string; name?: string }) {
return {}; // or prisma.user.create({ data })
}
}
Feature module — wires controller and service:
// src/users/users.module.ts
import { Module } from '@nestjs/common';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
@Module({
controllers: [UsersController],
providers: [UsersService],
exports: [UsersService], // if other modules need UsersService
})
export class UsersModule {}
7. Routing and Parameters
- Path params:
@Param('id', ParseIntPipe) id: number - Query:
@Query('page') page: string,@Query() query: { page?: string; limit?: string } - Body:
@Body() body: CreateUserDto(use DTOs withclass-validatorfor validation) - Headers:
@Headers('authorization') auth: string
8. DTOs and Validation
npm install class-validator class-transformer
Enable global validation in main.ts:
import { ValidationPipe } from '@nestjs/common';
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe({ whitelist: true }));
DTO example:
// src/users/dto/create-user.dto.ts
import { IsEmail, IsOptional, IsString } from 'class-validator';
export class CreateUserDto {
@IsEmail()
email: string;
@IsOptional()
@IsString()
name?: string;
}
In controller: @Body() body: CreateUserDto — invalid body returns 400 automatically.
9. Exception Handling
- Use built-in exceptions:
NotFoundException,BadRequestException,UnauthorizedException, etc. - Throwing in a service or controller is caught by Nest and converted to HTTP response.
import { NotFoundException } from '@nestjs/common';
throw new NotFoundException('User not found');
// -> 404 { "statusCode": 404, "message": "User not found" }
- Custom exception filter: implement
ExceptionFilterand use@Catch()/@UseFilters()for global or per-route behavior.
10. Dependency Injection
- Constructor injection:
constructor(private readonly usersService: UsersService) {} - Services are providers; register in a module's
providers(or use@Injectable()and register in a module). - Other modules: import
UsersModuleand, if needed, exportUsersServicefrom it so it can be injected elsewhere.
11. OpenAPI (Swagger)
npm install @nestjs/swagger swagger-ui-express
// main.ts
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
const config = new DocumentBuilder()
.setTitle('My API')
.setVersion('1.0')
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('docs', app, document);
Use @ApiTags(), @ApiBody(), @ApiResponse() on controllers and methods to enrich docs.
12. Database (Prisma)
npm install @prisma/client
npm install -D prisma
npx prisma init
- Define schema in
prisma/schema.prisma, thennpx prisma generateand migrations. - Create a PrismaService that extends
PrismaClientand is provided in a module (e.g.PrismaModule). - Inject
PrismaServicein your services and callthis.prisma.user.findMany(), etc.
13. Project Structure (Scalable)
src/
main.ts
app.module.ts
users/
users.module.ts
users.controller.ts
users.service.ts
dto/
create-user.dto.ts
update-user.dto.ts
prisma/
prisma.module.ts
prisma.service.ts
prisma/
schema.prisma
14. Running and Building
npm run start # dev
npm run start:dev # watch mode
npm run start:prod # production (after build)
npm run build # compile to dist/
Lesson Plan
- Create project and understand module/controller/service.
- Add a feature module (e.g. users) with CRUD.
- DTOs and validation (class-validator).
- Exception handling and status codes.
- Prisma (or TypeORM) integration.
- OpenAPI (Swagger).
- Auth (e.g. JWT with Passport).
Comparison: Express vs Nest.js
| Aspect | Express (Node.js) | Nest.js |
|---|---|---|
| Structure | You define | Modules, DI, layers |
| TypeScript | Optional | First-class |
| Validation | Manual / Zod | DTOs + class-validator |
| OpenAPI | Manual / swagger-jsdoc | Built-in Swagger |
| Learning | Easier, minimal | More concepts |
Use Express for small APIs or maximum flexibility; use Nest.js for larger, structured TypeScript backends.
Next
- 01_restful_api — REST
- 04_nodejs — Node.js with Express
- 06_sql / 07_nosql — Databases