☰ Mục lục series
Tài liệu NestJS tiếng Việt
  1. Nhập môn (Overview)
    1. Giới thiệu
    2. First steps
    3. Controllers
    4. Providers
    5. Modules
    6. Middleware
    7. Exception filters
    8. Pipes
    9. Guards
    10. Interceptors
    11. Custom decorators
  2. Nền tảng (Fundamentals)
    1. Custom providers
    2. Asynchronous providers
    3. Dynamic modules
    4. Injection scopes
    5. Circular dependency
    6. Module reference
    7. Lazy-loading modules
    8. Execution context
    9. Lifecycle events
    10. Discovery service
    11. Platform agnosticism
    12. Testing
  3. Kỹ thuật (Techniques)
    1. Configuration
    2. Database (SQL)
    3. MongoDB
    4. Validation
    5. Caching
    6. Serialization
    7. Versioning
    8. Task scheduling
    9. Queues
    10. Logging
    11. Cookies
    12. Events
    13. Compression
    14. File upload
    15. Streaming files
    16. HTTP module
    17. Session
    18. MVC
    19. Performance (Fastify)
    20. Server-Sent Events
  4. Bảo mật (Security)
    1. Authentication
    2. Authorization
    3. Encryption & Hashing
    4. Helmet
    5. CORS
    6. CSRF
    7. Rate limiting
    8. Vòng đời request
  5. OpenAPI (Swagger)
    1. Introduction
    2. Types and parameters
    3. Operations
    4. Security
    5. Mapped types
    6. Decorators
    7. CLI plugin
    8. Other features
  6. Recipes
    1. REPL
    2. CRUD generator
    3. SWC (fast compiler)
    4. Passport (auth)
    5. Hot reload
    6. MikroORM
    7. Router module
    8. Health checks (Terminus)
    9. CQRS
    10. Prisma
    11. Serve static
    12. Commander
    13. Async local storage
  7. WebSockets
    1. Gateways
    2. Exception filters
    3. Pipes
    4. Guards
    5. Interceptors
    6. Adapters
  8. Microservices
    1. Overview
    2. Redis
    3. MQTT
    4. NATS
    5. RabbitMQ
    6. Kafka
    7. gRPC
    8. Custom transporters
    9. Exception filters
    10. Pipes
    11. Pre-request hooks
    12. Guards
    13. Interceptors
  9. GraphQL
    1. Quick start
    2. Resolvers
    3. Mutations
    4. Subscriptions
    5. Scalars
    6. Directives
    7. Interfaces
    8. Unions and enums
    9. Field middleware
    10. Mapped types
    11. Plugins
    12. Complexity
    13. Extensions
    14. CLI plugin
    15. Generating SDL
    16. Sharing models
    17. Other features
    18. Federation
  10. CLI & triển khai
    1. CLI overview
    2. Workspaces
    3. Libraries
    4. Usage
    5. Scripts
    6. Deployment
    7. Standalone apps
  11. FAQ
    1. Serverless
    2. HTTP adapter
    3. Keep-Alive connections
    4. Global path prefix
    5. Raw body
    6. Hybrid application
    7. HTTPS & multiple servers
    8. Common errors
  12. Phụ lục
    1. Bảng thuật ngữ
Nền tảng (Fundamentals)

Discovery service

Bản dịch từ docs.nestjs.com/fundamentals/discovery-service

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.

Nội dung gốc: NestJS Documentation © Kamil Myśliwiec, giấy phép MIT. Bản dịch và ghi chú: Lê Văn Dũng.