NestJS Complete Guide — Enterprise Node.js
Advertisement
NestJS brings enterprise patterns to Node.js. It's opinionated, well-structured, and perfect for large teams building complex applications.
NestJS Architecture
NestJS uses three main components:
- Controllers: Handle HTTP requests
- Services: Business logic
- Modules: Organize code
npm install -g @nestjs/cli
nest new my-app
cd my-app
npm run start:dev
Controllers
import { Controller, Get, Post, Body, Param } from "@nestjs/common";
import { UserService } from "./user.service";
@Controller("users")
export class UserController {
constructor(private userService: UserService) {}
@Get()
findAll() {
return this.userService.findAll();
}
@Get(":id")
findOne(@Param("id") id: string) {
return this.userService.findOne(parseInt(id));
}
@Post()
create(@Body() createUserDto: CreateUserDto) {
return this.userService.create(createUserDto);
}
}
- NestJS Architecture
- Controllers
- Services
- Modules
- Dependency Injection
- Decorators and Guards
- Middleware
- DTOs (Data Transfer Objects)
- Database Integration with TypeORM
- Exception Handling
- Testing
- FAQ
Services
import { Injectable } from "@nestjs/common";
@Injectable()
export class UserService {
private users = [
{ id: 1, name: "Alice", email: "alice@example.com" },
];
findAll() {
return this.users;
}
findOne(id: number) {
return this.users.find((user) => user.id === id);
}
create(createUserDto: CreateUserDto) {
const user = { id: 2, ...createUserDto };
this.users.push(user);
return user;
}
}
Modules
import { Module } from "@nestjs/common";
import { UserService } from "./user.service";
import { UserController } from "./user.controller";
@Module({
imports: [],
controllers: [UserController],
providers: [UserService],
exports: [UserService], // Export for other modules
})
export class UserModule {}
// app.module.ts
import { Module } from "@nestjs/common";
import { UserModule } from "./user/user.module";
@Module({
imports: [UserModule],
})
export class AppModule {}
Dependency Injection
interface Logger {
log(message: string): void;
}
@Injectable()
class ConsoleLogger implements Logger {
log(message: string): void {
console.log(message);
}
}
@Module({
providers: [
{
provide: "Logger",
useClass: ConsoleLogger,
},
],
})
export class AppModule {}
@Injectable()
export class UserService {
constructor(@Inject("Logger") private logger: Logger) {}
findAll() {
this.logger.log("Finding all users");
return [];
}
}
Decorators and Guards
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;
}
}
@Controller("admin")
@UseGuards(AuthGuard)
export class AdminController {
@Get()
getAdminData() {
return { admin: true };
}
}
Middleware
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.path}`);
next();
}
}
@Module({
imports: [UserModule],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes(
{ path: "users", method: RequestMethod.GET },
{ path: "users/:id", method: RequestMethod.GET }
);
}
}
DTOs (Data Transfer Objects)
import { IsString, IsEmail, MinLength } from "class-validator";
export class CreateUserDto {
@IsString()
@MinLength(3)
name: string;
@IsEmail()
email: string;
@IsOptional()
@IsInt()
age: number;
}
@Controller("users")
export class UserController {
@Post()
create(@Body() createUserDto: CreateUserDto) {
// Validation happens automatically
return this.userService.create(createUserDto);
}
}
Database Integration with TypeORM
npm install @nestjs/typeorm typeorm sqlite3
import { Module } from "@nestjs/common";
import { TypeOrmModule } from "@nestjs/typeorm";
import { User } from "./entities/user.entity";
import { UserService } from "./user.service";
import { UserController } from "./user.controller";
@Module({
imports: [
TypeOrmModule.forRoot({
type: "sqlite",
database: "db.sqlite",
entities: [User],
synchronize: true,
}),
TypeOrmModule.forFeature([User]),
],
controllers: [UserController],
providers: [UserService],
})
export class AppModule {}
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column()
email: string;
}
@Injectable()
export class UserService {
constructor(
@InjectRepository(User)
private userRepository: Repository<User>
) {}
findAll() {
return this.userRepository.find();
}
create(createUserDto: CreateUserDto) {
const user = this.userRepository.create(createUserDto);
return this.userRepository.save(user);
}
}
Exception Handling
import { HttpException, HttpStatus } from "@nestjs/common";
@Get(":id")
getUser(@Param("id") id: string) {
const user = this.userService.findOne(parseInt(id));
if (!user) {
throw new HttpException("User not found", HttpStatus.NOT_FOUND);
}
return user;
}
// Custom exception filter
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const status = exception.getStatus();
response.status(status).json({
statusCode: status,
message: exception.getResponse(),
});
}
}
// Use in main.ts
app.useGlobalFilters(new HttpExceptionFilter());
Testing
import { Test, TestingModule } from "@nestjs/testing";
import { UserController } from "./user.controller";
import { UserService } from "./user.service";
describe("UserController", () => {
let controller: UserController;
let service: UserService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [UserController],
providers: [
{
provide: UserService,
useValue: {
findAll: jest.fn().mockReturnValue([]),
},
},
],
}).compile();
controller = module.get<UserController>(UserController);
service = module.get<UserService>(UserService);
});
it("should find all users", () => {
expect(controller.findAll()).toEqual([]);
});
});
FAQ
Q: Is NestJS overkill for small projects? A: Probably. Use Express for small APIs, NestJS for team projects with multiple developers.
Q: Can I use NestJS with MongoDB? A: Yes, use @nestjs/mongoose instead of TypeORM.
Q: What's the performance of NestJS? A: Similar to Express. NestJS builds on Express by default. You can use Fastify adapter for better performance.
NestJS brings structure and scalability to Node.js backend development. It's perfect for teams and large projects where consistency and maintainability matter.
Advertisement