# 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. - Published: 2026-05-26 - Updated: 2026-05-27 - Author: SharpSkill - Tags: nestjs, microservices, grpc, nodejs, typescript, architecture - Reading time: 9 min --- 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 11, natywnej obsłudze gRPC oraz automatycznej propagacji trace ID framework dostarcza wszystko, czego potrzeba do rozbicia monolitu na dobrze odgraniczone serwisy bez utraty komfortu pracy programisty. > **gRPC kontra REST w komunikacji wewnętrznej** > > 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. 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. ```typescript // orders.controller.ts 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ą. ## 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ć. ```protobuf // proto/users.proto 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: ```typescript // main.ts 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( 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: ```typescript // users.controller.ts 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 — pomyśl o strumieniach cen w czasie rzeczywistym, paginowanych kursorach bazy danych czy aktualizacjach 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: ```typescript // users.controller.ts — server streaming 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 { // 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. > **Obsługa błędów w strumieniowaniu** > > 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. ## 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](https://docs.nestjs.com/faq/hybrid-application) łączy wiele transportów z jedną instancją NestJS: ```typescript // main.ts — hybrid application 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({ 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. ## 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](https://martinfowler.com/bliki/BoundedContext.html): 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 `Order` i `OrderLine` zawsze zmieniają się razem, należą do tego samego serwisu. Jeśli `User` i `Order` zmieniają 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.created` przez Kafkę lub RabbitMQ. Serwis magazynowy reaguje, a serwis zamówień nie musi wiedzieć o jego istnieniu. - **Współdzielone pakiety proto dla kontraktów.** Pliki `.proto` należ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 11 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: ```typescript // orders.service.ts 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](https://github.com/nodeshift/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. > **Propagacja trace ID w NestJS 11** > > NestJS 11 automatycznie propaguje trace ID przez transporty Kafka, RabbitMQ i gRPC. W połączeniu z [OpenTelemetry](https://opentelemetry.io/docs/languages/js/) umożliwia to pełne śledzenie rozproszone bez niestandardowych interceptorów — znacząca poprawa względem NestJS 10, gdzie wymagana była ręczna propagacja kontekstu. ## 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. **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. Dla głębszej praktyki we wzorcach architektury NestJS moduł [moduły i wstrzykiwanie zależności w NestJS](/technologies/node-nestjs/interview-questions/nestjs-modules-di) omawia podstawy DI, które stanowią fundament kompozycji mikroserwisów. Moduł [middleware i interceptory](/technologies/node-nestjs/interview-questions/middleware-interceptors) zajmuje się zagadnieniami przekrojowymi współdzielonymi między transportami. ## Podsumowanie - Abstrakcja transportowa NestJS 11 pozwala serwisom przełączać się między TCP, gRPC, Kafką i NATS bez przepisywania logiki handlerów — dekoratory `@MessagePattern` i `@EventPattern` dział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 — NestJS mapuje każdą emisję Observable na wiadomość strumienia gRPC - 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 --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/pl/blog/node-nestjs/nestjs-microservices-grpc-architecture