Có nhiều cách để khởi chạy một ứng dụng Nest. Bạn có thể tạo một web app, một microservice, hoặc đơn giản là một Nest standalone application (ứng dụng standalone, không có bất kỳ network listener nào). Standalone application của Nest là một lớp bọc quanh IoC container của Nest — nơi lưu giữ tất cả các class đã được khởi tạo. Chúng ta có thể lấy reference đến bất kỳ instance nào đã tồn tại từ bên trong bất kỳ module nào đã import, trực tiếp thông qua đối tượng standalone application. Nhờ đó, bạn có thể tận dụng framework Nest ở bất cứ đâu, ví dụ như trong các CRON job dạng script. Bạn thậm chí có thể xây dựng một CLI dựa trên nó.
Ghi chú người dịch Đây chính là cách nhiều tool nội bộ (seed database, chạy migration thủ công, viết script one-off) tận dụng được toàn bộ DI container của Nest mà không cần khởi động HTTP server. Nếu bạn từng viết script Node độc lập rồi tự require/import service theo kiểu thủ công, đây là cách làm “đúng chuẩn Nest” thay cho việc đó — inject vẫn hoạt động bình thường vì IoC container vẫn được dựng lên đầy đủ.
Getting started (Bắt đầu)
Để tạo một Nest standalone application, dùng cú pháp sau:
async function bootstrap() {
const app = await NestFactory.createApplicationContext(AppModule);
// logic ứng dụng của bạn ở đây ...
}
await bootstrap();
Retrieving providers from static modules (Lấy provider từ module tĩnh)
Đối tượng standalone application cho phép bạn lấy reference đến bất kỳ instance nào đã được đăng ký trong Nest application. Giả sử chúng ta có một provider TasksService trong module TasksModule, được import bởi module AppModule của chúng ta. Class này cung cấp một tập hợp các phương thức mà chúng ta muốn gọi từ bên trong một CRON job.
const tasksService = app.get(TasksService);
Để truy cập instance TasksService, chúng ta dùng phương thức get(). Phương thức get() hoạt động giống như một query (truy vấn) tìm kiếm một instance trong từng module đã đăng ký. Bạn có thể truyền vào đó token của bất kỳ provider nào. Ngoài ra, để kiểm tra ngữ cảnh (context) một cách nghiêm ngặt, hãy truyền một object options với thuộc tính strict: true. Khi bật tùy chọn này, bạn phải điều hướng qua các module cụ thể để lấy một instance nhất định từ context đã chọn.
const tasksService = app.select(TasksModule).get(TasksService, { strict: true });
Dưới đây là tóm tắt các phương thức có sẵn để lấy reference đến instance từ đối tượng standalone application.
get()
|
Lấy một instance của controller hoặc provider (bao gồm cả guard, filter, v.v.) có sẵn trong application context. |
select()
|
Điều hướng qua đồ thị (graph) của các module để lấy ra một instance cụ thể của module đã chọn (dùng cùng với strict mode như đã mô tả ở trên). |
Gợi ý Ở chế độ non-strict (không nghiêm ngặt), root module sẽ được chọn mặc định. Để chọn một module khác, bạn cần tự điều hướng qua đồ thị module theo từng bước.
Hãy nhớ rằng standalone application không có bất kỳ network listener nào, vì vậy mọi tính năng của Nest liên quan đến HTTP (ví dụ: middleware, interceptor, pipe, guard, v.v.) đều không khả dụng trong context này.
Ví dụ, ngay cả khi bạn đăng ký một global interceptor trong ứng dụng của mình, rồi sau đó lấy instance của một controller bằng phương thức app.get(), thì interceptor đó cũng sẽ không được thực thi.
Ghi chú người dịch Đây là điểm dễ gây nhầm lẫn nhất khi vibe-code với standalone app: bạn có thể nghĩ “app đã bootstrap thì middleware/guard/interceptor phải chạy” — nhưng thực chất createApplicationContext() chỉ dựng DI container, không dựng HTTP adapter (Express/Fastify) phía sau nó. Nó gần giống việc bạn new Container() trong một framework DI thuần (ví dụ Awilix ở Node, hay tự tay wire dependency qua constructor trong Go) mà không kèm theo router — router/middleware là một tầng hoàn toàn khác, không tự động có mặt.
Retrieving providers from dynamic modules (Lấy provider từ dynamic module)
Khi làm việc với dynamic module, chúng ta cần truyền vào app.select chính đối tượng đại diện cho dynamic module đã đăng ký trong ứng dụng. Ví dụ:
export const dynamicConfigModule = ConfigModule.register({ folder: './config' });
@Module({
imports: [dynamicConfigModule],
})
export class AppModule {}
Sau đó bạn có thể chọn module đó ở bước tiếp theo:
const configService = app.select(dynamicConfigModule).get(ConfigService, { strict: true });
Terminating phase (Giai đoạn kết thúc)
Nếu bạn muốn ứng dụng Node đóng lại sau khi script chạy xong (ví dụ: đối với một script chạy CRON job), bạn phải gọi phương thức app.close() ở cuối hàm bootstrap của mình như sau:
async function bootstrap() {
const app = await NestFactory.createApplicationContext(AppModule);
// logic ứng dụng...
await app.close();
}
await bootstrap();
Và như đã đề cập trong chương Lifecycle events, việc này sẽ kích hoạt các lifecycle hook.
Example (Ví dụ)
Một ví dụ hoạt động thực tế có sẵn tại đây.