Trong loạt bài này, bạn sẽ học những nền tảng cốt lõi của Nest. Để làm quen với các khối xây dựng (building block) thiết yếu của ứng dụng Nest, chúng ta sẽ xây dựng một ứng dụng CRUD cơ bản với các tính năng bao quát khá nhiều nội dung ở mức nhập môn.
Ngôn ngữ
Chúng tôi rất thích TypeScript, nhưng trên hết - chúng tôi yêu Node.js. Đó là lý do Nest tương thích với cả TypeScript lẫn JavaScript thuần. Nest tận dụng các tính năng ngôn ngữ mới nhất, nên để dùng với JavaScript thuần (vanilla JavaScript), chúng ta cần trình biên dịch Babel.
Chúng tôi sẽ chủ yếu dùng TypeScript trong các ví dụ, nhưng bạn luôn có thể chuyển đổi code snippet sang cú pháp JavaScript thuần (chỉ cần click vào nút chuyển ngôn ngữ ở góc trên bên phải của mỗi snippet).
Yêu cầu tiên quyết
Hãy đảm bảo rằng Node.js đã được cài đặt trên hệ điều hành của bạn. Chạy một ứng dụng Nest yêu cầu v20.19 trở lên (hoặc v22.12+ đối với nhánh 22.x); các generator của Nest CLI yêu cầu v22.22.3+, v24.15+, hoặc v26+. Phiên bản LTS mới nhất đang được active thỏa mãn cả hai yêu cầu trên và đó cũng là phiên bản chúng tôi khuyến nghị.
Thiết lập
Thiết lập một project mới khá đơn giản với Nest CLI. Sau khi đã cài npm, bạn có thể tạo một project Nest mới bằng các lệnh sau trong terminal của hệ điều hành:
$ npm i -g @nestjs/cli
$ nest new project-name
CLI sẽ hỏi bạn muốn tạo project theo CommonJS hay ESM. Các starter dùng ESM sẽ mặc định dùng Vitest và oxlint.
Gợi ý Để tạo một project mới với bộ tính năng nghiêm ngặt hơn (stricter) của TypeScript, hãy truyền flag --strict vào lệnh nest new.
Thư mục project-name sẽ được tạo ra, các node module cùng một số file boilerplate khác sẽ được cài đặt, và một thư mục src/ sẽ được tạo và điền sẵn một số file cốt lõi.
Dưới đây là tổng quan ngắn gọn về các file cốt lõi đó:
app.controller.ts |
Một controller cơ bản với một route duy nhất. |
app.controller.spec.ts |
Các unit test cho controller. |
app.module.ts |
Module gốc (root module) của ứng dụng. |
app.service.ts |
Một service cơ bản với một method duy nhất. |
main.ts |
File khởi đầu (entry file) của ứng dụng, sử dụng hàm cốt lõi NestFactory để tạo một instance ứng dụng Nest. |
Ghi chú người dịch Test runner phụ thuộc lựa chọn lúc nest new: project ESM dùng Vitest, project CommonJS dùng Jest (vai trò tương tự PHPUnit trong Laravel hay package testing built-in của Go) - Nest sinh sẵn file test ngay từ lúc nest new, khác với Express nơi bạn phải tự thêm test runner sau.
File main.ts chứa một hàm async, dùng để bootstrap ứng dụng của chúng ta:
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);
}
await bootstrap();
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);
}
await bootstrap();
Để tạo một instance ứng dụng Nest, chúng ta dùng class cốt lõi NestFactory. NestFactory expose một vài static method cho phép bạn tạo instance ứng dụng. Method create() trả về một application object, thỏa mãn interface INestApplication. Object này cung cấp một tập hợp các method được mô tả trong các chương tiếp theo. Trong ví dụ main.ts ở trên, chúng ta chỉ đơn giản khởi động HTTP listener, để ứng dụng chờ nhận các request HTTP gửi đến.
Ghi chú người dịch Khác với Express nơi const app = express() gần như không làm gì cho tới khi bạn gọi listen(), NestFactory.create() đã dựng xong toàn bộ dependency graph (module, provider, controller) và khởi tạo IoC container trước khi HTTP server thực sự lắng nghe. Vì vậy nếu thiếu dependency, ứng dụng sẽ crash ngay ở bước create(), trước khi kịp listen().
Lưu ý rằng một project được scaffold bằng Nest CLI sẽ tạo ra cấu trúc project ban đầu khuyến khích developer tuân theo convention: giữ mỗi module trong thư mục riêng của nó.
Gợi ý Mặc định, nếu có lỗi xảy ra trong quá trình tạo ứng dụng, app của bạn sẽ thoát với mã lỗi 1. Nếu bạn muốn nó ném ra exception thay vì thoát, hãy tắt option abortOnError (ví dụ: NestFactory.create(AppModule, { abortOnError: false })).
Nền tảng (Platform)
Nest hướng tới việc trở thành một framework độc lập với nền tảng (platform-agnostic). Tính độc lập về nền tảng giúp tạo ra các phần logic có thể tái sử dụng mà developer có thể tận dụng trên nhiều loại ứng dụng khác nhau. Về mặt kỹ thuật, Nest có thể hoạt động với bất kỳ Node HTTP framework nào một khi có adapter tương ứng được tạo ra. Có hai nền tảng HTTP được hỗ trợ sẵn: express và fastify. Bạn có thể chọn nền tảng phù hợp nhất với nhu cầu của mình.
platform-express |
Express là một web framework tối giản nổi tiếng dành cho node. Đây là thư viện đã qua thử thách thực tế (battle tested), sẵn sàng cho production, với nhiều tài nguyên do cộng đồng triển khai. Package @nestjs/platform-express được dùng mặc định. Đa số người dùng chỉ cần dùng Express là đủ, không cần thao tác gì thêm để kích hoạt nó. |
platform-fastify |
Fastify là một framework hiệu năng cao, overhead thấp, tập trung tối đa vào hiệu quả và tốc độ. Đọc cách sử dụng tại đây. |
Dù dùng nền tảng nào, nó cũng sẽ expose application interface riêng của mình. Chúng được xem lần lượt là NestExpressApplication và NestFastifyApplication.
Ghi chú người dịch Cơ chế adapter này giống http.Handler interface trong Go - code business logic (controller, service) không cần biết mình đang chạy trên framework HTTP nào, miễn adapter tương ứng implement đúng interface Nest yêu cầu. Nhờ vậy đổi từ Express sang Fastify thường chỉ cần sửa vài dòng ở main.ts, không phải sửa lại controller/service.
Khi bạn truyền một type vào method NestFactory.create(), như ví dụ dưới đây, object app sẽ có các method chỉ dành riêng cho nền tảng đó. Tuy nhiên, lưu ý rằng bạn không cần chỉ định type trừ khi bạn thực sự muốn truy cập API của nền tảng bên dưới.
const app = await NestFactory.create<NestExpressApplication>(AppModule);
Chạy ứng dụng
Sau khi hoàn tất quá trình cài đặt, bạn có thể chạy lệnh sau tại command prompt của hệ điều hành để khởi động ứng dụng, sẵn sàng nhận các request HTTP gửi đến:
$ npm run start
Gợi ý Để tăng tốc quá trình phát triển (build nhanh hơn tới 20 lần), bạn có thể dùng SWC builder bằng cách truyền flag -b swc vào script start, như sau: npm run start -- -b swc.
Lệnh này khởi động app với HTTP server lắng nghe trên port được định nghĩa trong file src/main.ts. Sau khi ứng dụng đã chạy, hãy mở trình duyệt và truy cập http://localhost:3000/. Bạn sẽ thấy thông điệp Hello World!.
Để theo dõi thay đổi trong các file của bạn, bạn có thể chạy lệnh sau để khởi động ứng dụng:
$ npm run start:dev
Lệnh này sẽ theo dõi các file của bạn, tự động biên dịch lại và reload server.
Linting và formatting
CLI cung cấp giải pháp tốt nhất có thể để scaffold một quy trình phát triển đáng tin cậy ở quy mô lớn. Vì vậy, một project Nest được tạo ra sẽ đi kèm sẵn cả linter lẫn formatter (lần lượt là oxlint và prettier).
Gợi ý Chưa rõ vai trò của formatter so với linter? Tìm hiểu sự khác biệt tại đây.
Để đảm bảo một quy trình phát triển mặc định nhanh chóng, các project mới được tạo sẽ dùng oxlint kết hợp với prettier.
Đối với các môi trường headless nơi IDE không liên quan (Continuous Integration, Git hooks, v.v.), một project Nest đi kèm sẵn các npm script sẵn sàng sử dụng.
# Lint bằng oxlint
$ npm run lint
# Format bằng prettier
$ npm run format