DiscoveryService được cung cấp bởi package @nestjs/core là một tiện ích mạnh mẽ, cho phép developer kiểm tra và truy xuất (inspect/retrieve) động các provider, controller, và metadata khác trong một ứng dụng NestJS. Điều này đặc biệt hữu ích khi xây dựng plugin, decorator, hay các tính năng nâng cao dựa vào introspection lúc runtime. Bằng cách tận dụng DiscoveryService, developer có thể xây dựng kiến trúc linh hoạt và module hóa hơn, tạo điều kiện cho tự động hóa và hành vi động trong ứng dụng.
Ghi chú người dịch “Introspection lúc runtime” ở đây tương tự reflection trong Go (reflect package) hoặc decorator metadata trong TypeScript nói chung: thay vì bạn code cứng “biết trước” danh sách provider nào cần xử lý, DiscoveryService cho phép bạn quét toàn bộ DI container lúc ứng dụng đang chạy để tìm ra chúng dựa trên metadata gắn kèm.
Bắt đầu
Trước khi dùng DiscoveryService, bạn cần import DiscoveryModule vào module nơi bạn định dùng nó. Điều này đảm bảo service khả dụng cho dependency injection. Dưới đây là ví dụ cách cấu hình nó trong một module NestJS:
import { Module } from '@nestjs/common';
import { DiscoveryModule } from '@nestjs/core';
import { ExampleService } from './example.service.js';
@Module({
imports: [DiscoveryModule],
providers: [ExampleService],
})
export class ExampleModule {}
Sau khi module được thiết lập, DiscoveryService có thể được inject vào bất kỳ provider hoặc service nào cần discovery động.
@Injectable()
export class ExampleService {
constructor(private readonly discoveryService: DiscoveryService) {}
}
@Injectable()
@Dependencies(DiscoveryService)
export class ExampleService {
constructor(discoveryService) {
this.discoveryService = discoveryService;
}
}
Khám phá provider và controller
Một trong những khả năng chính của DiscoveryService là truy xuất tất cả provider đã đăng ký trong ứng dụng. Điều này hữu ích để xử lý provider một cách động dựa trên các điều kiện cụ thể. Đoạn code sau minh họa cách truy cập tất cả provider:
const providers = this.discoveryService.getProviders();
console.log(providers);
Mỗi object provider chứa thông tin như instance, token, và metadata của nó. Tương tự, nếu bạn cần truy xuất tất cả controller đã đăng ký trong ứng dụng, bạn có thể làm như sau:
const controllers = this.discoveryService.getControllers();
console.log(controllers);
Tính năng này đặc biệt hữu ích cho các tình huống mà controller cần được xử lý động, chẳng hạn như theo dõi analytics (analytics tracking), hoặc cơ chế tự động đăng ký.
Trích xuất metadata
Ngoài việc khám phá provider và controller, DiscoveryService còn cho phép truy xuất metadata gắn trên các component này. Điều này đặc biệt có giá trị khi làm việc với custom decorator lưu metadata lúc runtime.
Ví dụ, xét trường hợp một custom decorator được dùng để gắn tag metadata cụ thể cho provider:
import { DiscoveryService } from '@nestjs/core';
export const FeatureFlag = DiscoveryService.createDecorator();
Áp dụng decorator này lên một service cho phép nó lưu metadata, sau này có thể được truy vấn:
import { Injectable } from '@nestjs/common';
import { FeatureFlag } from './custom-metadata.decorator.js';
@Injectable()
@FeatureFlag('experimental')
export class CustomService {}
Ghi chú người dịch Nếu bạn từng dùng decorator/annotation kiểu Java (Spring) hay attribute trong C#: DiscoveryService.createDecorator() tạo ra một decorator tùy biến hoạt động tương tự - gắn một “nhãn” (label) kèm giá trị lên class ở compile time, rồi truy vấn lại nhãn đó lúc runtime. Khác với việc bạn tự viết Reflect.defineMetadata thủ công, cách này được NestJS đóng gói sẵn, tiện hơn khi kết hợp với DiscoveryService.
Sau khi metadata được gắn vào provider theo cách này, DiscoveryService giúp việc lọc provider dựa trên metadata đã gán trở nên dễ dàng. Đoạn code sau minh họa cách truy xuất các provider đã được tag với một giá trị metadata cụ thể:
const providers = this.discoveryService.getProviders();
const [provider] = providers.filter(
(item) =>
this.discoveryService.getMetadataByDecorator(FeatureFlag, item) ===
'experimental',
);
console.log(
'Providers with the "experimental" feature flag metadata:',
provider,
);
Kết luận
DiscoveryService là một công cụ đa năng và mạnh mẽ giúp thực hiện introspection lúc runtime trong ứng dụng NestJS. Bằng cách cho phép khám phá động provider, controller, và metadata, nó đóng vai trò quan trọng trong việc xây dựng framework mở rộng được, plugin, và các tính năng dựa trên tự động hóa. Dù bạn cần quét và xử lý provider, trích xuất metadata để xử lý nâng cao, hay tạo kiến trúc module hóa, có thể mở rộng, DiscoveryService cung cấp một cách tiếp cận hiệu quả và có cấu trúc để đạt được các mục tiêu này.