Mikroserwisy w NestJS w 2026: architektura, gRPC i pytania rekrutacyjne
Praktyczny przewodnik po architekturze mikroserwisów NestJS z gRPC: granice serwisów, warstwy transportowe, wzorce strumieniowania oraz najczęstsze pytania rekrutacyjne na 2026 rok.

Mikroserwisy NestJS stały się standardowym podejściem do budowy rozproszonych backendów Node.js w 2026 roku. Dzięki ulepszonej warstwie transporterów w NestJS 12, natywnej obsłudze gRPC, filtrom wyjątków dla wywołań RPC oraz automatycznej propagacji trace ID framework dostarcza wszystko, czego potrzeba do rozbicia monolitu na dobrze odgraniczone serwisy bez utraty komfortu pracy programisty.
gRPC korzysta z Protocol Buffers oraz HTTP/2, oferując nawet 10-krotnie szybszą serializację i natywne strumieniowanie dwukierunkowe w porównaniu z JSON przez REST. W komunikacji serwis-serwis wewnątrz klastra gRPC zmniejsza opóźnienia i wymusza ścisłe kontrakty poprzez pliki .proto. REST pozostaje lepszym wyborem dla publicznych API obsługiwanych przez przeglądarki i podmioty zewnętrzne.
Architektura warstwy transportowej mikroserwisów NestJS
Pakiet @nestjs/microservices zastępuje kontrolery HTTP handlerami zorientowanymi na wiadomości. Zamiast routingu po ścieżce URL każdy handler reaguje na wzorzec wiadomości (message pattern), ciąg znaków lub klucz obiektu, który identyfikuje operację. Ta abstrakcja sprawia, że ta sama logika handlera działa na TCP, Redisie, NATS, Kafce, RabbitMQ czy gRPC bez zmian w kodzie.
NestJS 11 wprowadził metodę unwrap() na wszystkich transporterach, dającą bezpośredni dostęp do bazowej instancji klienta. Rozwiązuje to długotrwały problem: inspekcję stanu połączenia, strojenie interwałów keepalive czy dostęp do funkcji specyficznych dla brokera, których abstrakcja NestJS nie udostępnia. NestJS 12 rozbudowuje to o hooki przed żądaniem, pozwalające handlerom wykonać własną logikę bezpośrednio przed wywołaniem dla zagadnień przekrojowych takich jak sprawdzanie autoryzacji czy metryki.
Dwa wzorce komunikacji napędzają mikroserwisy NestJS:
- Żądanie-odpowiedź (
@MessagePattern): klient wysyła wiadomość i czeka na odpowiedź. Odpowiednie dla zapytań synchronicznych, takich jak pobranie profilu użytkownika czy weryfikacja tokenu. - Zdarzeniowy (
@EventPattern): klient emituje zdarzenie i kontynuuje pracę, nie oczekuje odpowiedzi. Idealne dla logów audytowych, powiadomień czy wyzwalania procesów po stronie odbiorcy.
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 automatycznie serializuje wartość zwracaną i odsyła ją przez transport. Handler @EventPattern nie zwraca niczego, NestJS odrzuca każdą wartość zwracaną.
Począwszy od NestJS 12 transporty TCP, Redis, NATS, MQTT i RabbitMQ logują ostrzeżenie o deprecjacji, gdy handler @EventPattern odpowiada na ClientProxy#send(). To zachowanie zmieni się w następnej wersji głównej, zgodnie z istniejącym podejściem Kafki. Należy zmigrować handlery zdarzeń do prawdziwej semantyki fire-and-forget lub przełączyć się na @MessagePattern dla przepływów żądanie-odpowiedź.
Konfiguracja gRPC jako transportu mikroserwisu NestJS
Integracja gRPC w NestJS zaczyna się od pliku .proto, który definiuje kontrakt serwisu. Protocol Buffers wymuszają bezpieczeństwo typów na poziomie sieci, zarówno klient, jak i serwer muszą zgodzić się na dokładny kształt każdej wiadomości, zanim choćby jeden bajt przejdzie przez sieć.
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;
}Po stronie serwera aplikacja NestJS uruchamia mikroserwis gRPC, wskazując plik proto i wiążąc adres sieciowy:
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();Kontroler używa @GrpcMethod zamiast @MessagePattern. Dekorator przyjmuje nazwę serwisu i nazwę metody RPC jako argumenty, mapując je bezpośrednio na definicję proto:
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 automatycznie obsługuje ładowanie proto, serializację i zarządzanie połączeniem HTTP/2. Metoda kontrolera otrzymuje zwykłe obiekty TypeScript, bez ręcznego dekodowania proto.
Wzorce strumieniowania gRPC w NestJS
Strumieniowanie po stronie serwera pasuje do scenariuszy, w których pojedyncze żądanie generuje wiele wyników w czasie: strumienie cen w czasie rzeczywistym, paginowane kursory bazy danych czy aktualizacje postępu podczas długo trwającego zadania. Handler zwraca Observable z RxJS, a NestJS strumieniuje każdą wyemitowaną wartość do klienta jako osobną wiadomość gRPC:
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,
})),
);
}Strumieniowanie dwukierunkowe korzysta z @GrpcStreamCall(). Zarówno klient, jak i serwer wysyłają niezależne strumienie, przydatne w systemach czatu, edycji współbieżnej czy zwielokrotnionym pozyskiwaniu telemetrii. Handler otrzymuje instancję grpc.ServerDuplexStream i samodzielnie zarządza cyklem odczytu/zapisu.
NestJS 12 dodaje obsługę backpressure: strumienie żądań wstrzymują się, gdy ich odpowiedzi czekają na zapis. Zapobiega to przepełnieniu bufora, gdy wolny konsument nie nadąża za szybkim producentem.
Obserwator (Observable), który ulegnie awarii wewnątrz strumienia gRPC, kończy całe wywołanie ze statusem CANCELLED. Logikę strumienia należy opakować w operatory catchError i emitować odpowiednie dla gRPC metadane błędu. Nieobsłużone błędy strumienia są przyczyną numer jeden tajemniczych zerwań połączeń w produkcyjnych serwisach NestJS gRPC.
Filtry wyjątków gRPC w NestJS 12
NestJS 12 wprowadza dedykowaną obsługę wyjątków dla gRPC, odzwierciedlając znany wzorzec filtrów wyjątków HTTP. Klasy wyjątków specyficzne dla statusu pozwalają handlerom zwracać natywne dla gRPC kody błędów zamiast generycznych awarii:
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',
}));
}
}Filtr można zastosować globalnie lub per kontroler, aby zapewnić spójne odpowiedzi błędów we wszystkich endpointach gRPC. W połączeniu z istniejącą klasą RpcException zapewnia to parytet obsługi błędów między transportami HTTP i gRPC.
Aplikacje hybrydowe: HTTP i gRPC w jednym serwisie
Wiele rzeczywistych serwisów NestJS potrzebuje zarówno publicznego REST API, jak i wewnętrznych endpointów gRPC. Wzorzec aplikacji hybrydowej łączy wiele transportów z jedną instancją NestJS:
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();To podejście pozwala uniknąć wdrażania osobnych serwisów, gdy pojedynczy odgraniczony kontekst potrzebuje zarówno interfejsu zewnętrznego, jak i wewnętrznego. Ten sam kontener wstrzykiwania zależności, strażnicy, interceptory i pipe'y obowiązują dla obu transportów, co ogranicza duplikację i zachowuje spójność zachowania.
Gotowy na rozmowy o Node.js / NestJS?
Ćwicz z naszymi interaktywnymi symulatorami, flashcards i testami technicznymi.
Wzorce regexp dla Kafki w NestJS 12
NestJS 12 pozwala @MessagePattern() i @EventPattern() akceptować wyrażenia regularne dla tematów Kafki. Pojedynczy handler może teraz dynamicznie subskrybować wiele tematów:
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);
}
}Zmniejsza to boilerplate, gdy wiele tematów współdzieli tę samą logikę handlera. Walidacja klucza wzorca w NestJS 12 wymusza maksymalnie 256 znaków: dłuższe wzorce kończą się cichą porażką i nie pasują do żadnych handlerów.
Migracja NATS v3 w NestJS 12
NestJS 12 zastępuje klienta NATS oficjalnym pakietem @nats-io/transport-node. To zmiana łamiąca kompatybilność: deserializery otrzymują teraz pełny obiekt wiadomości zamiast surowego payloadu.
// Przed (NestJS 11)
deserialize(value: any): any {
return JSON.parse(value);
}
// Po (NestJS 12): odczyt payloadu przez msg.json()
deserialize(msg: any): any {
return msg.json();
}Przy migracji do NestJS 12 należy zaktualizować wszystkie niestandardowe deserializery NATS. Nowy klient zapewnia lepszą wydajność i zgodność z oficjalnym ekosystemem NATS.
Granice serwisów i Domain-Driven Design w NestJS
Rozbicie monolitu na mikroserwisy bez jasnych granic tworzy rozproszony monolit, najgorsze cechy obu architektur. Każdy mikroserwis NestJS powinien posiadać pojedynczy odgraniczony kontekst: własną bazę danych, własne modele domenowe i własny cykl życia wdrożenia.
Praktyczne wskazówki dotyczące wyznaczania granic serwisów w NestJS:
- Jeden moduł NestJS na korzeń agregatu. Jeśli
OrderiOrderLinezawsze zmieniają się razem, należą do tego samego serwisu. JeśliUseriOrderzmieniają się niezależnie, zasługują na osobne serwisy. - Zdarzenia asynchroniczne dla efektów ubocznych między domenami. Gdy zostaje złożone zamówienie, należy wyemitować zdarzenie
order.createdprzez Kafkę lub RabbitMQ. Serwis magazynowy reaguje, a serwis zamówień nie musi wiedzieć o jego istnieniu. - Współdzielone pakiety proto dla kontraktów. Pliki
.protonależy przechowywać we wspólnym repozytorium. Zarówno producent, jak i konsument generują typy z tego samego źródła prawdy, rozjazd staje się błędem CI, a nie incydentem produkcyjnym.
Opcje mikroserwisów z kontenera DI w NestJS 12 sprawiają, że konfiguracja serwisu jest testowalna. Zamiast wpisywać na sztywno adresy brokera w main.ts, można wstrzyknąć ConfigService i podmieniać ciągi połączeń per środowisko bez przebudowy.
Wzorce niezawodności: timeouty, ponowienia i bezpieczniki
Wewnętrzne wywołania gRPC bez terminów (deadline) zamieniają pojedynczą wolną zależność w awarię całego systemu. Każde wywołanie ClientProxy.send() powinno zawierać 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 };
}
}Ponowienia powinny dotyczyć wyłącznie operacji idempotentnych. Ponowienie wywołania CreateOrder grozi powstaniem duplikatów zamówień. Ponowienie zapytania FindOne jest bezpieczne, ponieważ nie wywołuje efektów ubocznych.
W zakresie bezpieczników (circuit breaker) biblioteka opossum dobrze integruje się z NestJS. Wywołania ClientGrpc należy opakować w bezpiecznik, który otwiera się po N kolejnych awariach i zamyka po konfigurowalnym czasie resetu.
NestJS 12 kontynuuje automatyczną propagację trace ID wprowadzoną w NestJS 11 przez transporty Kafka, RabbitMQ i gRPC. W połączeniu z OpenTelemetry umożliwia to pełne śledzenie rozproszone bez niestandardowych interceptorów.
Pytania rekrutacyjne o mikroserwisy NestJS
Rozmowy techniczne na stanowiska backendowe coraz częściej sprawdzają wiedzę o mikroserwisach obok wzorców specyficznych dla frameworka. Poniższe pytania obejmują koncepcje najczęściej poruszane na rozmowach skupionych na NestJS.
Jaka jest różnica między @MessagePattern a @EventPattern?
@MessagePattern realizuje żądanie-odpowiedź: klient wysyła wiadomość i blokuje się, aż otrzyma odpowiedź. @EventPattern realizuje model fire-and-forget: klient emituje zdarzenie i natychmiast kontynuuje wykonanie. Pod spodem @MessagePattern zwraca wynik handlera przez transport, podczas gdy @EventPattern odrzuca wartości zwracane. NestJS 12 zdeprecjonował odpowiadanie z handlerów @EventPattern na większości transportów.
Jak NestJS ładuje i wiąże pliki .proto dla gRPC?
NestJS używa pakietu @grpc/proto-loader do parsowania plików .proto przy starcie i generowania deskryptorów serwisów. Konfiguracja GrpcOptions określa ścieżkę proto i nazwę pakietu. Kontrolery oznaczone @GrpcMethod('ServiceName', 'MethodName') są dopasowywane do sparsowanych deskryptorów. Jeśli metoda zadeklarowana w proto nie ma pasującego handlera, NestJS zgłasza błąd przy bootstrapie.
Kiedy gRPC powinno zastąpić REST między mikroserwisami?
gRPC pasuje do wewnętrznych wywołań serwis-serwis, gdzie liczą się bezpieczeństwo typów, wydajność i strumieniowanie. Protocol Buffers tworzą mniejsze ładunki i szybszą serializację niż JSON. Multipleksowanie HTTP/2 zmniejsza narzut połączeń. REST pozostaje preferowany dla publicznych API, klientów przeglądarkowych i integracji zewnętrznych, gdzie czytelność dla człowieka i szerokie wsparcie narzędzi przeważają nad surową wydajnością.
Jak działa wzorzec aplikacji hybrydowej?
NestFactory.create() uruchamia serwer HTTP. Wywołanie app.connectMicroservice() dołącza dodatkowe transporty, gRPC, Kafkę, Redisa lub dowolny wspierany transporter, do tej samej instancji aplikacji NestJS. Wszystkie transporty dzielą to samo drzewo modułów, kontener DI i potok middleware. app.startAllMicroservices() uruchamia wszystkie podłączone transportery, a app.listen() uruchamia warstwę HTTP.
Jakie wzorce niezawodności zapobiegają kaskadowym awariom?
Najważniejsze są trzy wzorce: timeouty (każde wywołanie RPC potrzebuje terminu), ponowienia z jitterem (tylko dla operacji idempotentnych, z wykładniczym backoffem plus losowym jitterem, aby uniknąć efektu stada), oraz bezpieczniki (otwarte po N awariach, półotwarte po okresie schłodzenia, zamknięte po pomyślnych testach zdrowotności). Integracja NestJS z RxJS sprawia, że timeouty i ponowienia można komponować przez operatory pipe.
Co zmieniło się dla mikroserwisów w NestJS 12?
NestJS 12 dodał filtry wyjątków gRPC dla obsługi błędów specyficznych dla statusu, wzorce regexp dla Kafki do obsługi wielu tematów, wsparcie NATS v3 z nowym pakietem klienta, hooki przed żądaniem dla handlerów mikroserwisów oraz ulepszone backpressure dla strumieni gRPC. Ostrzeżenie o deprecjacji @EventPattern sygnalizuje przyszłą zmianę zachowania, gdzie handlery zdarzeń nie będą już odpowiadać na wywołania send().
Dla głębszej praktyki we wzorcach architektury NestJS moduł moduły i wstrzykiwanie zależności w NestJS omawia podstawy DI, które stanowią fundament kompozycji mikroserwisów. Moduł middleware i interceptory zajmuje się zagadnieniami przekrojowymi współdzielonymi między transportami. Przewodnik NestJS i GraphQL omawia alternatywę dla gRPC w typowanych API serwisów.
Zacznij ćwiczyć!
Sprawdź swoją wiedzę z naszymi symulatorami rozmów i testami technicznymi.
Źródła
- NestJS v12 is Now Available — oficjalne ogłoszenie obejmujące filtry wyjątków gRPC, wzorce regexp dla Kafki, NATS v3, hooki przed żądaniem
- NestJS Releases — changelog zawierający backpressure dla strumieni gRPC, limity kluczy wzorców, deprecjację EventPattern
- NestJS Hybrid Application Documentation — oficjalny przewodnik łączenia transportów HTTP i mikroserwisowych
Kluczowe wnioski dotyczące mikroserwisów NestJS w 2026
- Abstrakcja transportowa NestJS 12 pozwala serwisom przełączać się między TCP, gRPC, Kafką i NATS bez przepisywania logiki handlerów: dekoratory
@MessagePatterni@EventPatterndziałają identycznie we wszystkich transportach - gRPC z Protocol Buffers wymusza ścisłe kontrakty między serwisami poprzez pliki
.proto, wychwytując błędy integracyjne na etapie kompilacji, a nie w produkcji - Aplikacje hybrydowe łączą HTTP i gRPC w jednej instancji NestJS, eliminując potrzebę wdrażania osobnych serwisów dla interfejsów publicznych i wewnętrznych
- Strumieniowanie po stronie serwera przez Observable z RxJS umożliwia strumienie danych w czasie rzeczywistym bez złożoności WebSocketów, a NestJS 12 dodaje obsługę backpressure dla wolnych konsumentów
- Każde wewnętrzne wywołanie RPC wymaga terminu, ponowienia dotyczą wyłącznie operacji idempotentnych, a bezpieczniki zapobiegają temu, by pojedynczy wolny serwis kaskadowo wywołał pełną awarię
- Granice serwisów wynikają z Domain-Driven Design: jeden odgraniczony kontekst na serwis, zdarzenia asynchroniczne dla komunikacji między domenami, współdzielone pakiety proto dla egzekwowania kontraktów
Zacznij ćwiczyć!
Sprawdź swoją wiedzę z naszymi symulatorami rozmów i testami technicznymi.
Znajdziesz błąd w Node.js / NestJS?
Prawdziwy fragment kodu, ukryty błąd, jedna próba dziennie. Bez konta, żeby spróbować.

Autor:
Anthony Fillion-MailletZałożyciel SharpSkill
Programista fullstack od ponad 10 lat. Prowadzi SharpSkill i odpowiada za wszystko, co się tu ukazuje.
Zaktualizowano 22 października 2026
Tagi
Udostępnij
Powiązane artykuły

NestJS + Prisma: nowoczesny stack backendowy dla Node.js
Kompletny przewodnik po budowaniu nowoczesnego API backendowego z NestJS i Prismą. Konfiguracja, modele, serwisy, transakcje i dobre praktyki.

NestJS: Budowanie kompletnego REST API
Kompletny przewodnik po budowaniu profesjonalnego REST API z NestJS. Kontrolery, serwisy, moduły, walidacja z class-validator i obsługa błędów z praktycznymi przykładami.

NestJS i GraphQL w 2026: Schematy, Resolvery i Pytania Rekrutacyjne
Kompletny poradnik NestJS GraphQL omawiający podejścia schema-first i code-first, resolvery, DataLoader oraz pytania rekrutacyjne w 2026 roku.