☰ 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ữ
FAQ

Hybrid application (application lai)

Bản dịch từ docs.nestjs.com/faq/hybrid-application

Hybrid application là application lắng nghe request từ hai hoặc nhiều nguồn khác nhau. Điều này có thể kết hợp một HTTP server với một microservice listener, hoặc thậm chí chỉ là nhiều microservice listener khác nhau. Phương thức createMicroservice mặc định không hỗ trợ nhiều server, nên trong trường hợp này mỗi microservice phải được tạo và khởi động thủ công. Để làm điều đó, instance INestApplication có thể được kết nối với các instance INestMicroservice thông qua phương thức connectMicroservice().

const app = await NestFactory.create(AppModule);
const microservice = app.connectMicroservice<MicroserviceOptions>({
  transport: Transport.TCP,
});

await app.startAllMicroservices();
await app.listen(3001);

Gợi ý phương thức app.listen(port) khởi động một HTTP server tại địa chỉ được chỉ định. Nếu application của bạn không xử lý HTTP request thì nên dùng phương thức app.init() thay thế.

Chú ý Thứ tự các lời gọi này rất quan trọng. Với await app.startAllMicroservices() được gọi trước (như trên), các microservice bắt đầu tiêu thụ message trước khi các lifecycle hook của application (như onModuleInit và onApplicationBootstrap) hoàn tất. Hãy gọi app.listen() (hoặc app.init()) trước startAllMicroservices() nếu handler của bạn không được phép nhận message cho đến khi mọi module đã khởi tạo xong hoàn toàn.

Ghi chú người dịch Đây là cạm bẫy thực tế hay gặp: nếu handler message của microservice phụ thuộc vào một provider được setup trong onModuleInit (ví dụ kết nối DB), mà startAllMicroservices() chạy trước, message đến sớm có thể bị xử lý khi provider đó chưa sẵn sàng, gây lỗi khó tái hiện lúc chạy thử nhưng dễ gặp khi traffic thật đến ngay sau khi app khởi động.

Để kết nối nhiều instance microservice, gọi connectMicroservice() cho từng microservice:

const app = await NestFactory.create(AppModule);
// microservice #1
const microserviceTcp = app.connectMicroservice<MicroserviceOptions>({
  transport: Transport.TCP,
  options: {
    port: 3001,
  },
});
// microservice #2
const microserviceRedis = app.connectMicroservice<MicroserviceOptions>({
  transport: Transport.REDIS,
  options: {
    host: 'localhost',
    port: 6379,
  },
});

await app.startAllMicroservices();
await app.listen(3001);

Để gắn @MessagePattern() chỉ cho một transport strategy duy nhất (ví dụ: MQTT) trong một hybrid application có nhiều microservice, ta có thể truyền thêm tham số thứ hai để xác định transport. Với các strategy có sẵn, đây là một giá trị của enum Transport; còn với custom transporter, đó là symbol transportId của class server tương ứng.

@MessagePattern('time.us.*', Transport.NATS)
getDate(@Payload() data: number[], @Ctx() context: NatsContext) {
  console.log(`Subject: ${context.getSubject()}`); // ví dụ: "time.us.east"
  return new Date().toLocaleTimeString(...);
}
@MessagePattern({ cmd: 'time.us' }, Transport.TCP)
getTCPDate(@Payload() data: number[]) {
  return new Date().toLocaleTimeString(...);
}
@MessagePattern('topic.time.us', XYZServer.Transport) // XYZServer là một custom transporter
getXYZDate(@Payload() data: number[]) {
  return new Date().toLocaleTimeString(...);
}
@Bind(Payload(), Ctx())
@MessagePattern('time.us.*', Transport.NATS)
getDate(data, context) {
  console.log(`Subject: ${context.getSubject()}`); // ví dụ: "time.us.east"
  return new Date().toLocaleTimeString(...);
}
@Bind(Payload(), Ctx())
@MessagePattern({ cmd: 'time.us' }, Transport.TCP)
getTCPDate(data, context) {
  return new Date().toLocaleTimeString(...);
}

@Bind(Payload())
@MessagePattern('topic.time.us', XYZServer.Transport)
getXYZDate(data) {
  return new Date().toLocaleTimeString(...);
}

Gợi ý @Payload(), @Ctx(), Transport và NatsContext được import từ @nestjs/microservices. XYZServer.Transport ở đây là symbol transportId mà custom transporter expose ra.

Chia sẻ cấu hình

Mặc định, một hybrid application sẽ không kế thừa các global pipe, interceptor, guard và filter đã cấu hình cho application chính (dựa trên HTTP). Để kế thừa các thuộc tính cấu hình này từ application chính, đặt thuộc tính inheritAppConfig trong tham số thứ hai (một options object tùy chọn) của lời gọi connectMicroservice(), như sau:

Ghi chú người dịch Mỗi microservice được connectMicroservice() tạo ra về bản chất là một INestMicroservice riêng, có “ngữ cảnh thực thi” (execution context) và pipeline enhancer (guard/pipe/interceptor/filter) tách biệt với app HTTP chính — giống việc bạn khởi tạo hai app Express độc lập rồi chạy chung process. inheritAppConfig: true chỉ đơn giản là copy lại config global đã set trên app chính sang, không phải cơ chế tự động ngầm.

const microservice = app.connectMicroservice<MicroserviceOptions>(
  {
    transport: Transport.TCP,
  },
  { inheritAppConfig: true },
);

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.