NestJS là gì? Framework Node.js có cấu trúc cho backend TypeScript (2026)
Khi phát triển backend với Node.js, bạn sẽ sớm gặp phải những vấn đề quen thuộc: dự án càng lớn thì càng khó quản lý code, mỗi thành viên trong team viết theo kiểu riêng, không có sự thống nhất về cấu trúc. Express.js rất linh hoạt nhưng chính sự tự do đó lại gây khó khăn khi dự án phát triển. NestJS ra đời để giải quyết chính xác những vấn đề đó.
Bài viết này sẽ giúp bạn hiểu NestJS là gì, tại sao nhiều team chọn NestJS, kiến trúc cốt lõi và cách bắt đầu xây dựng API thực tế với NestJS.
1. NestJS là gì?
NestJS là một progressive Node.js framework dùng để xây dựng các ứng dụng server-side có khả năng mở rộng cao (scalable). Framework này được thiết kế để giúp các team phát triển tạo ra backend có cấu trúc rõ ràng, dễ bảo trì và dễ kiểm thử.
NestJS được tạo ra bởi kỹ sư người Ba Lan Kamil Myśliwiec, ra mắt năm 2017 và hiện tại (2026) đã lên đến phiên bản 11.x. Với hơn 65.000 GitHub stars, đây là một trong những framework Node.js phổ biến nhất hiện nay.
Nếu phải tóm gọn NestJS trong một câu, đó là: "Angular cho phía backend". NestJS lấy cảm hứng từ kiến trúc của Angular — sử dụng modules, controllers, services và decorators theo cách tương tự. Điều này giúp những developer đã biết Angular có thể học NestJS rất nhanh.
Đặc điểm nổi bật của NestJS
- TypeScript-first: Được viết bằng TypeScript và ưu tiên TypeScript (vẫn hỗ trợ JavaScript)
- Kiến trúc có cấu trúc: Module/Controller/Service phân vai rõ ràng
- Dependency Injection tích hợp: IoC container được xây dựng sẵn
- Chạy trên Express hoặc Fastify: Tận dụng nền tảng HTTP server đã được kiểm chứng
- Hệ sinh thái phong phú: Hỗ trợ REST API, GraphQL, WebSocket, Microservices
Để hiểu NestJS tốt hơn, bạn cần nắm JavaScript cơ bản và đặc biệt là TypeScript — vì NestJS khai thác tối đa các tính năng của TypeScript như decorators và type system.
2. Tại sao nên dùng NestJS?
2.1 Cấu trúc chuẩn cho làm việc nhóm
Khi dùng Express.js để xây dựng dự án lớn, mỗi developer lại tổ chức code theo cách riêng. Người dùng MVC, người tạo service layer, người thì viết tất cả vào một file. Kết quả là khi team lớn lên, việc đọc và bảo trì code của nhau trở nên rất khó khăn.
NestJS giải quyết vấn đề này bằng cách quy định rõ where to put what: Controllers xử lý routing, Services chứa logic, Modules nhóm code liên quan. Developer mới join team cũng biết ngay phải tìm code ở đâu.
2.2 TypeScript native
NestJS được viết bằng TypeScript và được thiết kế để dùng với TypeScript. Bạn nhận được type safety, IDE auto-completion, refactoring dễ dàng ngay từ đầu mà không cần cấu hình thêm.
2.3 Dependency Injection tích hợp sẵn
DI (Dependency Injection) là một design pattern giúp code sạch hơn và dễ test hơn. Trong Express, bạn phải tự setup hoặc dùng thư viện bên ngoài. Với NestJS, DI container đã được tích hợp sẵn.
2.4 Dễ test
NestJS được thiết kế với testing trong tâm trí. DI giúp mock dependency dễ dàng, và framework cung cấp sẵn các utility cho unit test và e2e test.
2.5 Hệ sinh thái module phong phú
@nestjs/typeorm— Tích hợp TypeORM (PostgreSQL, MySQL, SQLite)@nestjs/mongoose— Tích hợp MongoDB@nestjs/graphql— Xây dựng GraphQL API@nestjs/jwt— JWT Authentication@nestjs/passport— Authentication strategy (OAuth, etc.)@nestjs/websockets— Realtime communication@nestjs/microservices— Microservices architecture
3. Kiến trúc NestJS: Modules, Controllers, Services
Ứng dụng NestJS được xây dựng từ 3 khái niệm chính: Modules, Controllers và Services. Hiểu rõ 3 thành phần này là chìa khóa để làm chủ NestJS.
Luồng xử lý request trong NestJS
Client gửi request
↓
[Controller] — Nhận request, xác định route
↓
[Service] — Xử lý business logic
↓
[Database] — Lấy / lưu dữ liệu (nếu cần)
↓
[Controller] — Trả về response
↓
Client nhận response
3.1 Modules — Tổ chức ứng dụng
Module là container nhóm các Controllers và Services liên quan lại với nhau. Mỗi module xử lý một tính năng cụ thể (users, products, orders…). Decorator dùng: @Module().
// users.module.ts
import { Module } from '@nestjs/common';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
@Module({
controllers: [UsersController], // Controllers của module này
providers: [UsersService], // Services / Providers của module này
exports: [UsersService], // Export để module khác có thể dùng
})
export class UsersModule {}
AppModule (root module) là điểm khởi đầu của ứng dụng, import tất cả các module khác:
// app.module.ts
import { Module } from '@nestjs/common';
import { UsersModule } from './users/users.module';
@Module({
imports: [UsersModule], // Import UsersModule vào ứng dụng
})
export class AppModule {}
3.2 Controllers — Xử lý HTTP request
Controller nhận HTTP request và định tuyến (routing) đến đúng handler. Không chứa business logic — chỉ nhận request và trả về response. Decorator dùng: @Controller(), @Get(), @Post()…
// users.controller.ts
import { Controller, Get, Post, Body, Param } from '@nestjs/common';
import { UsersService } from './users.service';
@Controller('users') // Tất cả route trong class này có prefix /users
export class UsersController {
// NestJS tự động inject UsersService vào đây (Dependency Injection)
constructor(private readonly usersService: UsersService) {}
@Get() // Xử lý GET /users
findAll() {
return this.usersService.findAll();
}
@Get(':id') // Xử lý GET /users/:id
findOne(@Param('id') id: string) {
return this.usersService.findOne(+id); // +id chuyển string → number
}
@Post() // Xử lý POST /users
create(@Body() createUserDto: any) {
return this.usersService.create(createUserDto);
}
}
3.3 Services — Chứa business logic
Service là nơi xử lý business logic và truy cập dữ liệu. Service có thể được inject vào bất kỳ Controller hoặc Service nào khác. Decorator dùng: @Injectable().
// users.service.ts
import { Injectable } from '@nestjs/common';
@Injectable() // Đánh dấu class này có thể được inject bởi DI container
export class UsersService {
// Dữ liệu mẫu trong bộ nhớ (thực tế sẽ dùng database)
private users = [
{ id: 1, name: 'Nguyen Van A', email: 'a@example.com' },
{ id: 2, name: 'Tran Thi B', email: 'b@example.com' },
];
findAll() {
return this.users;
}
findOne(id: number) {
return this.users.find(user => user.id === id);
}
create(createUserDto: { name: string; email: string }) {
const newUser = {
id: this.users.length + 1,
...createUserDto,
};
this.users.push(newUser);
return newUser;
}
}
4. Cài đặt và tạo project NestJS đầu tiên
Trước khi bắt đầu, bạn cần cài đặt Node.js. Nếu chưa có, xem hướng dẫn cài đặt Node.js.
4.1 Cài đặt NestJS CLI
# Cài NestJS CLI toàn cục (chỉ cần làm một lần)
npm install -g @nestjs/cli
# Kiểm tra đã cài thành công
nest --version
4.2 Tạo project mới
# Tạo project mới tên "my-api"
nest new my-api
# Vào thư mục project
cd my-api
# Chạy server ở chế độ development (tự reload khi code thay đổi)
npm run start:dev
Mở trình duyệt vào http://localhost:3000 sẽ thấy Hello World! — project đã chạy thành công.
4.3 Cấu trúc thư mục được tạo ra
my-api/
├── src/
│ ├── app.controller.ts # Controller mặc định
│ ├── app.module.ts # Root module
│ ├── app.service.ts # Service mặc định
│ └── main.ts # Entry point — khởi động ứng dụng
├── test/
│ └── app.e2e-spec.ts # E2E test
├── package.json
├── tsconfig.json
└── nest-cli.json
4.4 Tạo module, controller, service bằng CLI
# Tạo đầy đủ resource cho "users" (module + controller + service + DTO)
nest generate resource users
# Hoặc tạo từng phần
nest generate module users
nest generate controller users
nest generate service users
# Dạng rút gọn
nest g res products
nest g mo orders
nest g co orders
nest g s orders
5. Ví dụ thực tế: CRUD API với NestJS
Hãy xây dựng một API quản lý users hoàn chỉnh với đầy đủ các thao tác CRUD (Create, Read, Update, Delete).
5.1 DTO (Data Transfer Object)
// dto/create-user.dto.ts
export class CreateUserDto {
name: string; // Tên người dùng — bắt buộc
email: string; // Email — bắt buộc
age?: number; // Tuổi — không bắt buộc
}
// dto/update-user.dto.ts
export class UpdateUserDto {
name?: string;
email?: string;
age?: number;
}
5.2 Controller hoàn chỉnh
// users.controller.ts
import {
Controller, Get, Post, Put, Delete,
Body, Param, HttpCode, HttpStatus,
} from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Get() // GET /users
findAll() {
return this.usersService.findAll();
}
@Get(':id') // GET /users/:id
findOne(@Param('id') id: string) {
return this.usersService.findOne(+id);
}
@Post() // POST /users → 201 Created
@HttpCode(HttpStatus.CREATED)
create(@Body() createUserDto: CreateUserDto) {
return this.usersService.create(createUserDto);
}
@Put(':id') // PUT /users/:id
update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
return this.usersService.update(+id, updateUserDto);
}
@Delete(':id') // DELETE /users/:id → 204 No Content
@HttpCode(HttpStatus.NO_CONTENT)
remove(@Param('id') id: string) {
return this.usersService.remove(+id);
}
}
5.3 Service hoàn chỉnh
// users.service.ts
import { Injectable, NotFoundException } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
@Injectable()
export class UsersService {
private users = [
{ id: 1, name: 'Nguyen Van A', email: 'a@example.com', age: 25 },
{ id: 2, name: 'Tran Thi B', email: 'b@example.com', age: 22 },
];
private nextId = 3;
findAll() {
return this.users;
}
findOne(id: number) {
const user = this.users.find(u => u.id === id);
if (!user) {
throw new NotFoundException(`Không tìm thấy user với id ${id}`);
}
return user;
}
create(createUserDto: CreateUserDto) {
const newUser = { id: this.nextId++, ...createUserDto };
this.users.push(newUser);
return newUser;
}
update(id: number, updateUserDto: UpdateUserDto) {
const user = this.findOne(id);
const index = this.users.findIndex(u => u.id === id);
this.users[index] = { ...user, ...updateUserDto };
return this.users[index];
}
remove(id: number) {
this.findOne(id);
this.users = this.users.filter(u => u.id !== id);
}
}
6. Dependency Injection trong NestJS
Dependency Injection (DI) là một trong những tính năng quan trọng nhất của NestJS. Hiểu DI sẽ giúp bạn viết code sạch hơn và dễ test hơn nhiều.
DI là gì? Giải thích đơn giản
Hãy tưởng tượng bạn đang mở quán cà phê. Bạn cần một máy pha cà phê để làm việc. Có hai cách:
- Không có DI: Bạn tự mua máy pha cà phê (tự khởi tạo dependency trong class)
- Có DI: Ai đó mang máy pha cà phê đến cho bạn (dependency được "inject" từ bên ngoài)
Cách thứ hai linh hoạt hơn: muốn thay máy khác chỉ cần đổi máy mang đến — không cần thay đổi cách pha cà phê.
// Không có DI — tự tạo dependency (cách cũ)
class UsersController {
private usersService: UsersService;
constructor() {
this.usersService = new UsersService(); // Khó test, khó thay đổi
}
}
// Có DI — NestJS tự inject (cách NestJS)
@Controller('users')
class UsersController {
constructor(private readonly usersService: UsersService) {}
// NestJS nhìn vào kiểu dữ liệu → tìm UsersService trong container → tự inject
}
Lợi ích của DI khi viết test
// Khi test, inject mock thay vì service thật
const mockUsersService = {
findAll: jest.fn().mockReturnValue([
{ id: 1, name: 'Test User', email: 'test@example.com' }
]),
};
const module = await Test.createTestingModule({
controllers: [UsersController],
providers: [
{ provide: UsersService, useValue: mockUsersService },
],
}).compile();
7. NestJS vs Express vs Fastify — Khi nào nên chọn NestJS?
Nếu bạn đã biết Express.js, câu hỏi tự nhiên là: tại sao lại cần NestJS? Dưới đây là bảng so sánh chi tiết.
| Tiêu chí | Express | NestJS | Fastify |
|---|---|---|---|
| Cấu trúc | Tự do, không bắt buộc | Có cấu trúc cố định (Module/Controller/Service) | Tự do, plugin-based |
| TypeScript | Hỗ trợ nhưng cần cấu hình | TypeScript-first, built-in | Hỗ trợ nhưng cần cấu hình |
| Độ khó học | Thấp | Trung bình - Cao | Thấp - Trung bình |
| Hiệu năng | Trung bình | Tương đương Express (dùng Fastify adapter được) | Cao (~2x Express) |
| Dependency Injection | Không có | Tích hợp sẵn | Không có |
| Testing | Tự setup thủ công | Built-in testing utilities | Tự setup thủ công |
| Phù hợp với | Nhỏ, prototype, học Node.js | Vừa-lớn, team development, enterprise | API hiệu năng cao |
Khi nào chọn Express?
- Dự án nhỏ, cá nhân hoặc prototype nhanh
- Đang học Node.js backend từ đầu
- Cần sự linh hoạt tối đa trong thiết kế
Khi nào chọn NestJS?
- Dự án vừa đến lớn, làm việc theo nhóm
- Muốn dùng TypeScript đầy đủ ngay từ đầu
- Đã có kinh nghiệm với Angular
- Dự án dài hạn, cần bảo trì nhiều năm
- Cần GraphQL, WebSocket hoặc Microservices
Khi nào chọn Fastify?
- Hiệu năng là ưu tiên hàng đầu
- Lưu ý: NestJS cũng có thể chạy trên Fastify bằng
@nestjs/platform-fastify
8. Lộ trình học NestJS
NestJS không phải framework nên học đầu tiên. Để học hiệu quả, bạn cần có nền tảng trước. Xem thêm lộ trình backend 2026 để có cái nhìn tổng thể.
-
JavaScript cơ bản (2–4 tuần)
Biến, hàm, object, array, async/await, Promise, module (import/export).
-
TypeScript cơ bản (1–2 tuần)
Type annotation, interface, class, decorators. NestJS dùng TypeScript decorators rất nhiều.
-
Node.js + npm (1 tuần)
Cài đặt môi trường, hiểu cách Node.js hoạt động, quản lý package với npm.
-
Express.js cơ bản (1–2 tuần)
HTTP request/response, routing, middleware, REST API cơ bản.
-
NestJS (2–4 tuần)
CLI, cấu trúc project, Module/Controller/Service, Dependency Injection, CRUD API, database integration.
Tài nguyên học NestJS
- Official docs: docs.nestjs.com
- Udemy: "NestJS: The Complete Developer's Guide"
- YouTube: NestJS official channel, Traversy Media
9. Câu hỏi thường gặp (FAQ)
NestJS có khó học không?
NestJS khó hơn Express một chút do có nhiều khái niệm hơn (Module, DI, decorators). Nhưng nếu đã biết JavaScript và TypeScript, bạn có thể nắm cơ bản trong 2–4 tuần. Nếu đã biết Angular, NestJS sẽ rất dễ vì kiến trúc tương tự.
NestJS và Express khác nhau như thế nào?
Express là framework tối giản — bạn tự quyết định mọi thứ. NestJS là framework có quan điểm (opinionated) — cung cấp cấu trúc cố định với Module/Controller/Service. Thực ra NestJS chạy trên Express bên dưới, nên Express là nền tảng, NestJS là layer ở trên.
NestJS có thể dùng với JavaScript (không dùng TypeScript) không?
Về mặt kỹ thuật, có thể. Nhưng NestJS được tối ưu cho TypeScript và hầu hết tài liệu, ví dụ đều dùng TypeScript. Khuyến nghị: nếu dùng NestJS, hãy dùng TypeScript luôn.
NestJS phù hợp với dự án nào?
NestJS phù hợp nhất với: dự án team (từ 2 người trở lên), dự án dài hạn cần bảo trì nhiều năm, hệ thống enterprise, ứng dụng cần GraphQL/WebSocket/Microservices, và dự án coi trọng testing.
NestJS có dùng được với PostgreSQL, MongoDB không?
Có. NestJS cung cấp module tích hợp chính thức: @nestjs/typeorm cho SQL databases (PostgreSQL, MySQL, SQLite) và @nestjs/mongoose cho MongoDB. Với TypeORM + PostgreSQL, bạn có thể định nghĩa entity, viết migration và query database dễ dàng.
Biết NestJS, lương ở Việt Nam là bao nhiêu?
Năm 2026, backend developer với 1–2 năm kinh nghiệm NestJS có thể kỳ vọng mức lương 15–25 triệu VND/tháng. Senior với 3+ năm, thêm kỹ năng DevOps và database có thể đạt 30–50 triệu VND/tháng hoặc hơn.
10. Kết luận
NestJS là lựa chọn mạnh mẽ cho những developer muốn xây dựng backend Node.js có cấu trúc, maintainable và scalable với TypeScript. Framework này đặc biệt phát huy sức mạnh trong môi trường team và dự án dài hạn.
Tóm lại những điểm chính:
- NestJS là progressive Node.js framework, TypeScript-first, lấy cảm hứng từ Angular
- Kiến trúc rõ ràng: Module → Controller → Service
- Built-in Dependency Injection giúp code sạch và dễ test
- Phù hợp nhất cho team development và dự án vừa-lớn
- Hệ sinh thái phong phú: REST, GraphQL, WebSocket, Microservices
Nếu bạn chưa có môi trường, hãy bắt đầu với hướng dẫn cài đặt Node.js. Sau đó cài NestJS CLI và tạo project đầu tiên theo các bước trong bài này. Xem thêm lộ trình backend 2026 để lên kế hoạch học tập toàn diện.