Microservices với NestJS năm 2026: Kiến trúc, gRPC và Câu hỏi Phỏng vấn
Hướng dẫn thực tiễn về kiến trúc NestJS microservices với gRPC: service boundaries, transport layers, streaming patterns và câu hỏi phỏng vấn thường gặp năm 2026.

NestJS microservices đã trở thành phương pháp tiêu chuẩn để xây dựng các backend Node.js phân tán vào năm 2026. Với transport layer được cải tiến của NestJS 12, hỗ trợ gRPC first-class, exception filters cho RPC calls, và automatic trace ID propagation, framework này cung cấp mọi thứ cần thiết để chia nhỏ monolith thành các services có ranh giới rõ ràng mà không ảnh hưởng đến developer experience.
gRPC sử dụng Protocol Buffers và HTTP/2, mang lại serialization nhanh hơn tới 10 lần và native bidirectional streaming so với JSON over REST. Đối với giao tiếp service-to-service bên trong cluster, gRPC giảm latency và đảm bảo các contracts nghiêm ngặt thông qua các file .proto. REST vẫn là lựa chọn tốt hơn cho public-facing APIs được sử dụng bởi browsers và bên thứ ba.
Kiến trúc Transport Layer trong NestJS Microservices
Package @nestjs/microservices thay thế HTTP controllers bằng message-oriented handlers. Thay vì routing theo URL path, mỗi handler đáp ứng một message pattern, một string hoặc object key xác định operation. Abstraction này có nghĩa là cùng một handler logic hoạt động trên TCP, Redis, NATS, Kafka, RabbitMQ, hoặc gRPC mà không cần thay đổi code.
NestJS 11 giới thiệu method unwrap() trên tất cả transporters, cung cấp quyền truy cập trực tiếp vào underlying client instance. Điều này giải quyết một pain point lâu đời: kiểm tra connection state, điều chỉnh keepalive intervals, hoặc truy cập các tính năng đặc thù của broker mà NestJS abstraction không expose. NestJS 12 xây dựng trên đó với pre-request hooks, cho phép handlers thực thi custom logic ngay trước khi invocation cho các cross-cutting concerns như auth checks hoặc metrics.
Hai communication patterns điều khiển NestJS microservices:
- Request-response (
@MessagePattern): client gửi message và chờ reply. Phù hợp cho synchronous queries như lấy user profile hoặc validate token. - Event-based (
@EventPattern): client emit event và tiếp tục, không mong đợi response. Lý tưởng cho audit logs, notifications, hoặc trigger downstream workflows.
import { Controller } from '@nestjs/common';
import { MessagePattern, EventPattern, Payload } from '@nestjs/microservices';
import { OrdersService } from './orders.service';
import { CreateOrderDto } from './dto/create-order.dto';
@Controller()
export class OrdersController {
constructor(private readonly ordersService: OrdersService) {}
// Request-response: caller waits for the created order
@MessagePattern('order.create')
async createOrder(@Payload() data: CreateOrderDto) {
return this.ordersService.create(data);
}
// Event-based: fire and forget, no response returned
@EventPattern('order.shipped')
async handleOrderShipped(@Payload() data: { orderId: string }) {
await this.ordersService.markAsShipped(data.orderId);
}
}Handler @MessagePattern tự động serialize return value và gửi nó trở lại qua transport. Handler @EventPattern không trả về gì: NestJS loại bỏ bất kỳ return value nào.
Bắt đầu từ NestJS 12, các transports TCP, Redis, NATS, MQTT, và RabbitMQ ghi log deprecation warning khi một @EventPattern handler reply tới ClientProxy#send(). Behavior này sẽ thay đổi trong major version tiếp theo, phù hợp với cách tiếp cận hiện có của Kafka. Migrate các event handlers sang semantics fire-and-forget thực sự hoặc chuyển sang @MessagePattern cho request-response flows.
Cấu hình gRPC làm Transport cho NestJS Microservice
Tích hợp gRPC trong NestJS bắt đầu với file .proto định nghĩa service contract. Protocol Buffers đảm bảo type safety ở wire level: cả client và server phải đồng ý về exact shape của mỗi message trước khi một byte nào đi qua network.
syntax = "proto3";
package users;
service UsersService {
rpc FindOne (UserById) returns (User);
rpc FindMany (UserFilter) returns (stream User);
}
message UserById {
string id = 1;
}
message UserFilter {
string role = 1;
int32 limit = 2;
}
message User {
string id = 1;
string email = 2;
string name = 3;
string role = 4;
}Ở server side, ứng dụng NestJS bootstrap một gRPC microservice bằng cách trỏ đến proto file và binding network address:
import { NestFactory } from '@nestjs/core';
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
import { join } from 'path';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.createMicroservice<MicroserviceOptions>(
AppModule,
{
transport: Transport.GRPC,
options: {
package: 'users',
protoPath: join(__dirname, 'proto/users.proto'),
url: '0.0.0.0:5000',
},
},
);
await app.listen();
}
bootstrap();Controller sử dụng @GrpcMethod thay vì @MessagePattern. Decorator nhận service name và RPC method name làm arguments, mapping trực tiếp đến proto definition:
import { Controller } from '@nestjs/common';
import { GrpcMethod } from '@nestjs/microservices';
import { UsersService } from './users.service';
@Controller()
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@GrpcMethod('UsersService', 'FindOne')
async findOne(data: { id: string }) {
return this.usersService.findById(data.id);
}
}NestJS xử lý proto loading, serialization, và HTTP/2 connection management tự động. Controller method nhận plain TypeScript objects, không cần manual proto decoding.
Các mẫu Streaming trong gRPC với NestJS
Server streaming phù hợp với các tình huống khi một request tạo ra multiple results theo thời gian: real-time price feeds, paginated database cursors, hoặc progress updates trong một long-running task. Handler trả về RxJS Observable, và NestJS stream mỗi emitted value tới client như một gRPC message riêng biệt:
import { Observable, from } from 'rxjs';
import { map } from 'rxjs/operators';
import { GrpcMethod } from '@nestjs/microservices';
@GrpcMethod('UsersService', 'FindMany')
findMany(data: { role: string; limit: number }): Observable<any> {
// Stream users matching the filter one by one
const users$ = from(this.usersService.findByRole(data.role, data.limit));
return users$.pipe(
map((user) => ({
id: user.id,
email: user.email,
name: user.name,
role: user.role,
})),
);
}Bidirectional streaming sử dụng @GrpcStreamCall(). Cả client và server gửi independent streams, hữu ích cho chat systems, collaborative editing, hoặc multiplexed telemetry ingestion. Handler nhận một grpc.ServerDuplexStream instance và quản lý read/write cycle thủ công.
NestJS 12 thêm backpressure handling: request streams giờ pause trong khi replies của chúng đang chờ được write. Điều này ngăn buffer overflow khi slow consumer không thể theo kịp fast producer.
Một Observable bị crash bên trong gRPC stream kết thúc toàn bộ call với status CANCELLED. Wrap stream logic trong catchError operators và emit gRPC-appropriate error metadata. Unhandled stream errors là nguyên nhân số một của mysterious connection drops trong production NestJS gRPC services.
gRPC Exception Filters trong NestJS 12
NestJS 12 giới thiệu dedicated exception handling cho gRPC, phản ánh HTTP exception filter pattern quen thuộc. Status-specific exception classes cho phép handlers trả về gRPC-native error codes thay vì generic failures:
import { Catch, RpcExceptionFilter, ArgumentsHost } from '@nestjs/common';
import { Observable, throwError } from 'rxjs';
import { RpcException } from '@nestjs/microservices';
import { status } from '@grpc/grpc-js';
@Catch(RpcException)
export class GrpcExceptionFilter implements RpcExceptionFilter<RpcException> {
catch(exception: RpcException, host: ArgumentsHost): Observable<any> {
const error = exception.getError() as { code?: number; message?: string };
return throwError(() => ({
code: error.code ?? status.INTERNAL,
message: error.message ?? 'Internal error',
}));
}
}Áp dụng filter globally hoặc per-controller để đảm bảo error responses nhất quán trên tất cả gRPC endpoints. Kết hợp với class RpcException hiện có, điều này mang lại error handling parity giữa HTTP và gRPC transports.
Ứng dụng Hybrid: HTTP và gRPC trên cùng một Service
Nhiều real-world NestJS services cần cả public REST API và internal gRPC endpoints. Hybrid application pattern kết nối multiple transports với single NestJS instance:
import { NestFactory } from '@nestjs/core';
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
import { join } from 'path';
import { AppModule } from './app.module';
async function bootstrap() {
// HTTP server on port 3000
const app = await NestFactory.create(AppModule);
// gRPC microservice on port 5000
app.connectMicroservice<MicroserviceOptions>({
transport: Transport.GRPC,
options: {
package: 'users',
protoPath: join(__dirname, 'proto/users.proto'),
url: '0.0.0.0:5000',
},
});
await app.startAllMicroservices();
await app.listen(3000);
}
bootstrap();Cách tiếp cận này tránh việc deploy các services riêng biệt khi một bounded context cần cả external và internal interfaces. Cùng một dependency injection container, guards, interceptors, và pipes áp dụng cho cả hai transports, giảm duplication và giữ behavior nhất quán.
Sẵn sàng chinh phục phỏng vấn Node.js / NestJS?
Luyện tập với mô phỏng tương tác, flashcards và bài kiểm tra kỹ thuật.
Kafka Regexp Patterns trong NestJS 12
NestJS 12 cho phép @MessagePattern() và @EventPattern() chấp nhận regular expressions cho Kafka topics. Một handler giờ có thể subscribe tới multiple topics một cách dynamic:
import { Controller } from '@nestjs/common';
import { EventPattern, Payload, Ctx, KafkaContext } from '@nestjs/microservices';
@Controller()
export class AnalyticsController {
// Matches analytics.pageview, analytics.click, analytics.purchase, etc.
@EventPattern(/^analytics..*/)
handleAnalyticsEvent(
@Payload() data: Record<string, unknown>,
@Ctx() context: KafkaContext,
) {
const topic = context.getTopic();
console.log(`Received event on ${topic}`, data);
}
}Điều này giảm boilerplate khi multiple topics chia sẻ cùng handler logic. Lưu ý rằng pattern key validation trong NestJS 12 enforces tối đa 256 ký tự: patterns dài hơn fail silently và không match với handlers nào.
Migration NATS v3 trong NestJS 12
NestJS 12 thay thế NATS client bằng official @nats-io/transport-node package. Đây là breaking change: deserializers giờ nhận full message object thay vì raw payload.
// Before (NestJS 11)
deserialize(value: any): any {
return JSON.parse(value);
}
// After (NestJS 12): read payload via msg.json()
deserialize(msg: any): any {
return msg.json();
}Update tất cả custom NATS deserializers khi migrate sang NestJS 12. Client mới mang lại improved performance và alignment với official NATS ecosystem.
Ranh giới Service và Domain-Driven Design với NestJS
Chia nhỏ monolith thành microservices mà không có ranh giới rõ ràng tạo ra distributed monolith, điều tệ nhất của cả hai kiến trúc. Mỗi NestJS microservice nên sở hữu một bounded context duy nhất: database riêng, domain models riêng, và deployment lifecycle riêng.
Hướng dẫn thực tiễn để vẽ ranh giới service trong NestJS:
- Một module NestJS cho mỗi aggregate root. Nếu
OrdervàOrderLineluôn thay đổi cùng nhau, chúng thuộc về cùng một service. NếuUservàOrderthay đổi độc lập, chúng xứng đáng có services riêng biệt. - Async events cho cross-domain side effects. Khi order được đặt, emit event
order.createdqua Kafka hoặc RabbitMQ. Inventory service react mà không cần order service biết nó tồn tại. - Shared proto packages cho contracts. Lưu trữ các file
.prototrong shared repository. Cả producer và consumer generate types từ cùng một source of truth, nên drift trở thành CI failure, không phải production incident.
Microservice options từ DI container của NestJS 12 làm cho service configuration có thể test được. Thay vì hardcoding broker URLs trong main.ts, inject ConfigService và swap connection strings per environment mà không cần rebuild.
Các mẫu Reliability: Timeouts, Retries và Circuit Breakers
Internal gRPC calls không có deadlines biến một slow dependency thành system-wide outage. Mỗi ClientProxy.send() call nên bao gồm một timeout:
import { Inject, Injectable } from '@nestjs/common';
import { ClientGrpc } from '@nestjs/microservices';
import { firstValueFrom, timeout, retry } from 'rxjs';
@Injectable()
export class OrdersService {
private usersService: any;
constructor(@Inject('USERS_PACKAGE') private client: ClientGrpc) {}
onModuleInit() {
this.usersService = this.client.getService('UsersService');
}
async getOrderWithUser(orderId: string, userId: string) {
// 3-second deadline, 2 retries with exponential backoff
const user = await firstValueFrom(
this.usersService.findOne({ id: userId }).pipe(
timeout(3000),
retry({ count: 2, delay: (err, retryCount) => {
const jitter = Math.random() * 100;
return new Promise(r => setTimeout(r, 1000 * retryCount + jitter));
}}),
),
);
return { orderId, user };
}
}Retries chỉ nên áp dụng cho idempotent operations. Retry một CreateOrder call có nguy cơ duplicate orders. Retry một FindOne query an toàn vì nó không có side effects.
Đối với circuit breaker functionality, library opossum tích hợp tốt với NestJS. Wrap các ClientGrpc calls trong circuit breaker mở sau N consecutive failures và đóng sau configurable reset timeout.
NestJS 12 tiếp tục automatic trace ID propagation được giới thiệu trong NestJS 11 trên Kafka, RabbitMQ, và gRPC transports. Kết hợp với OpenTelemetry, điều này cho phép full distributed tracing mà không cần custom interceptors.
Câu hỏi phỏng vấn NestJS Microservices
Technical interviews cho các vị trí backend ngày càng kiểm tra kiến thức microservices cùng với framework-specific patterns. Các câu hỏi này bao gồm các concepts thường được hỏi nhất trong các cuộc phỏng vấn NestJS-focused.
Sự khác biệt giữa @MessagePattern và @EventPattern là gì?
@MessagePattern triển khai request-response: client gửi message và blocks cho đến khi nhận reply. @EventPattern triển khai fire-and-forget: client emit event và tiếp tục execution ngay lập tức. Bên trong, @MessagePattern trả về handler's result qua transport, trong khi @EventPattern discard return values. Lưu ý rằng NestJS 12 đã deprecate việc replying từ @EventPattern handlers trên hầu hết các transports.
NestJS load và bind các file .proto cho gRPC như thế nào?
NestJS sử dụng package @grpc/proto-loader để parse các file .proto lúc startup và generate service descriptors. GrpcOptions configuration chỉ định proto path và package name. Controllers được annotate với @GrpcMethod('ServiceName', 'MethodName') được matched với parsed descriptors. Nếu một method được khai báo trong proto không có matching handler, NestJS throws lúc bootstrap.
Khi nào gRPC nên thay thế REST giữa các microservices?
gRPC phù hợp với internal service-to-service calls khi type safety, performance, và streaming quan trọng. Protocol Buffers tạo ra payloads nhỏ hơn và serialization nhanh hơn JSON. HTTP/2 multiplexing giảm connection overhead. REST vẫn được ưa thích cho public APIs, browser clients, và third-party integrations khi human readability và broad tooling support quan trọng hơn raw performance.
Hybrid application pattern hoạt động như thế nào?
NestFactory.create() boots HTTP server. Gọi app.connectMicroservice() attach additional transports (gRPC, Kafka, Redis, hoặc bất kỳ supported transporter nào) tới cùng một NestJS application instance. Tất cả transports chia sẻ cùng module tree, DI container, và middleware pipeline. app.startAllMicroservices() starts tất cả connected transporters, và app.listen() starts HTTP layer.
Những reliability patterns nào ngăn cascading failures?
Ba patterns quan trọng nhất: timeouts (mỗi RPC call cần deadline), retries với jitter (chỉ cho idempotent operations, với exponential backoff cộng random jitter để tránh thundering herds), và circuit breakers (mở sau N failures, half-open sau cooldown, đóng khi health checks pass). RxJS integration của NestJS làm cho timeouts và retries composable qua pipe operators.
Những gì thay đổi cho microservices trong NestJS 12?
NestJS 12 thêm gRPC exception filters cho status-specific error handling, Kafka regexp patterns cho multi-topic handlers, NATS v3 support với new client package, pre-request hooks cho microservice handlers, và improved gRPC stream backpressure. @EventPattern deprecation warning báo hiệu một behavioral change trong tương lai khi event handlers sẽ không còn reply tới send() calls.
Để thực hành sâu hơn về NestJS architecture patterns, module NestJS modules and dependency injection bao gồm DI fundamentals làm nền tảng cho microservice composition. Module middleware and interceptors đề cập đến cross-cutting concerns được chia sẻ giữa các transports. Hướng dẫn NestJS and GraphQL bao gồm một alternative cho gRPC cho typed service APIs.
Bắt đầu luyện tập!
Kiểm tra kiến thức với mô phỏng phỏng vấn và bài kiểm tra kỹ thuật.
Sources
- NestJS v12 is Now Available — thông báo chính thức bao gồm gRPC exception filters, Kafka regexp patterns, NATS v3, pre-request hooks
- NestJS Releases — changelog bao gồm gRPC stream backpressure, pattern key limits, EventPattern deprecation
- NestJS Hybrid Application Documentation — hướng dẫn chính thức để kết hợp HTTP và microservice transports
Điểm chính về NestJS Microservices năm 2026
- Transport abstraction của NestJS 12 cho phép services chuyển đổi giữa TCP, gRPC, Kafka, và NATS mà không cần viết lại handler logic: decorators
@MessagePatternvà@EventPatternhoạt động giống hệt nhau trên tất cả transports - gRPC với Protocol Buffers đảm bảo strict contracts giữa services thông qua các file
.proto, bắt integration errors lúc compile time thay vì trong production - Hybrid applications kết hợp HTTP và gRPC trên cùng một NestJS instance, loại bỏ nhu cầu deploy separate services cho public và internal interfaces
- Server streaming qua RxJS Observables cho phép real-time data feeds mà không có WebSocket complexity, với NestJS 12 thêm backpressure handling cho slow consumers
- Mỗi internal RPC call yêu cầu deadline, retries chỉ áp dụng cho idempotent operations, và circuit breakers ngăn một slow service từ cascading thành full outage
- Ranh giới service theo domain-driven design: một bounded context per service, async events cho cross-domain communication, shared proto packages cho contract enforcement
Bắt đầu luyện tập!
Kiểm tra kiến thức với mô phỏng phỏng vấn và bài kiểm tra kỹ thuật.
Bạn có tìm ra lỗi trong Node.js / NestJS không?
Một đoạn mã thật, một lỗi ẩn, mỗi ngày một lượt. Không cần tài khoản để thử.

Viết bởi
Anthony Fillion-MailletNgười sáng lập SharpSkill
Lập trình viên fullstack hơn 10 năm. Anh điều hành SharpSkill và chịu trách nhiệm về mọi nội dung đăng tại đây.
Cập nhật ngày 22 tháng 10, 2026
Thẻ
Chia sẻ
Bài viết liên quan

NestJS + Prisma: stack backend hiện đại cho Node.js
Hướng dẫn đầy đủ để xây dựng API backend hiện đại với NestJS và Prisma. Cấu hình, model, service, transaction và các best practice được giải thích chi tiết.

NestJS: Xây dựng REST API hoàn chỉnh từ đầu
Hướng dẫn đầy đủ xây dựng REST API chuyên nghiệp với NestJS. Controller, Service, Module, xác thực dữ liệu với class-validator và xử lý lỗi được giải thích chi tiết.

NestJS và GraphQL năm 2026: Schema, Resolver và Câu hỏi Phỏng vấn
Hướng dẫn toàn diện về tích hợp NestJS GraphQL với phương pháp schema-first và code-first, các pattern resolver, cùng câu hỏi phỏng vấn cho vị trí senior developer.