# Мікросервіси на NestJS у 2026: архітектура, gRPC та питання співбесід > Практичний посібник з архітектури мікросервісів NestJS на gRPC: межі сервісів, транспортні рівні, патерни стрімінгу та поширені питання співбесід на 2026 рік. - Published: 2026-05-26 - Updated: 2026-05-27 - Author: SharpSkill - Tags: nestjs, microservices, grpc, nodejs, typescript, architecture - Reading time: 9 min --- Мікросервіси NestJS стали стандартним підходом до побудови розподілених бекендів на Node.js у 2026 році. Завдяки покращеному рівню транспортерів у NestJS 11, першокласній підтримці gRPC та автоматичному поширенню trace ID фреймворк надає все необхідне для розбиття моноліту на чітко відмежовані сервіси без втрати зручності для розробника. > **gRPC проти REST для внутрішніх сервісів** > > gRPC використовує Protocol Buffers і HTTP/2, забезпечуючи до 10 разів швидшу серіалізацію та нативний двонаправлений стрімінг порівняно з JSON через REST. Для взаємодії сервіс-сервіс усередині кластера gRPC зменшує затримку та забезпечує суворі контракти через файли `.proto`. REST залишається кращим вибором для публічних API, які споживають браузери та сторонні системи. ## Архітектура транспортного рівня мікросервісів NestJS Пакет `@nestjs/microservices` замінює HTTP-контролери обробниками, орієнтованими на повідомлення. Замість маршрутизації за URL-шляхом кожен обробник реагує на **патерн повідомлення** (message pattern) — рядок або ключ обʼєкта, що ідентифікує операцію. Ця абстракція означає, що та сама логіка обробника працює через TCP, Redis, NATS, Kafka, RabbitMQ чи gRPC без змін у коді. NestJS 11 запровадив метод `unwrap()` на всіх транспортерах, що надає прямий доступ до базового екземпляра клієнта. Це вирішує давню проблему: інспекцію стану зʼєднання, налаштування інтервалів keepalive чи доступ до специфічних для брокера можливостей, які абстракція NestJS не розкриває. Два патерни комунікації керують мікросервісами NestJS: - **Запит-відповідь** (`@MessagePattern`): клієнт надсилає повідомлення й чекає на відповідь. Підходить для синхронних запитів на кшталт отримання профілю користувача чи перевірки токена. - **На основі подій** (`@EventPattern`): клієнт випромінює подію й рухається далі — відповіді не очікується. Ідеально для журналів аудиту, сповіщень чи запуску процесів на стороні споживача. ```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); } } ``` Обробник `@MessagePattern` автоматично серіалізує повернуте значення й надсилає його назад через транспорт. Обробник `@EventPattern` нічого не повертає — NestJS відкидає будь-яке повернуте значення. ## Налаштування gRPC як транспорту мікросервісу NestJS Інтеграція gRPC у NestJS починається з файлу `.proto`, який визначає контракт сервісу. Protocol Buffers забезпечують безпеку типів на рівні мережі — і клієнт, і сервер мають узгодити точну форму кожного повідомлення, перш ніж бодай один байт пройде мережею. ```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; } ``` На стороні сервера застосунок NestJS завантажує мікросервіс gRPC, вказуючи на файл proto та привʼязуючи мережеву адресу: ```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(); ``` Контролер використовує `@GrpcMethod` замість `@MessagePattern`. Декоратор приймає назву сервісу та назву методу RPC як аргументи, відображаючи їх безпосередньо на визначення 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 автоматично обробляє завантаження proto, серіалізацію та керування зʼєднанням HTTP/2. Метод контролера отримує звичайні обʼєкти TypeScript — ручне декодування proto не потрібне. ## Патерни стрімінгу gRPC у NestJS Серверний стрімінг підходить для сценаріїв, де один запит породжує кілька результатів з часом — як-от стрічки цін у реальному часі, посторінкові курсори бази даних чи оновлення прогресу під час тривалого завдання. Обробник повертає `Observable` з RxJS, а NestJS стрімить кожне випромінене значення клієнту як окреме повідомлення 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, })), ); } ``` Двонаправлений стрімінг використовує `@GrpcStreamCall()`. І клієнт, і сервер надсилають незалежні потоки — корисно для чат-систем, спільного редагування чи мультиплексованого збору телеметрії. Обробник отримує екземпляр `grpc.ServerDuplexStream` і керує циклом читання/запису вручну. > **Обробка помилок у стрімінгу** > > Observable, який аварійно завершився всередині потоку gRPC, припиняє весь виклик зі статусом `CANCELLED`. Логіку потоку слід обгортати операторами `catchError` і випромінювати відповідні для gRPC метадані помилки. Необроблені помилки потоку — причина номер один загадкових розривів зʼєднання у продакшн-сервісах NestJS gRPC. ## Гібридні застосунки: HTTP і gRPC в одному сервісі Багато реальних сервісів NestJS потребують як публічного REST API, так і внутрішніх ендпойнтів gRPC. [Патерн гібридного застосунку](https://docs.nestjs.com/faq/hybrid-application) підʼєднує кілька транспортів до одного екземпляра 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(); ``` Цей підхід дозволяє уникнути розгортання окремих сервісів, коли один відмежований контекст потребує і зовнішнього, і внутрішнього інтерфейсів. Той самий контейнер впровадження залежностей, ґарди, перехоплювачі та пайпи застосовуються до обох транспортів — це зменшує дублювання й зберігає узгодженість поведінки. ## Межі сервісів і Domain-Driven Design у NestJS Розбиття моноліту на мікросервіси без чітких меж створює розподілений моноліт — найгірше з обох архітектур. Кожен мікросервіс NestJS має володіти єдиним [відмежованим контекстом](https://martinfowler.com/bliki/BoundedContext.html): власною базою даних, власними доменними моделями та власним життєвим циклом розгортання. Практичні рекомендації щодо проведення меж сервісів у NestJS: - **Один модуль NestJS на корінь агрегату.** Якщо `Order` і `OrderLine` завжди змінюються разом, вони належать до одного сервісу. Якщо `User` і `Order` змінюються незалежно, вони заслуговують на окремі сервіси. - **Асинхронні події для побічних ефектів між доменами.** Коли оформлено замовлення, слід випромінити подію `order.created` через Kafka чи RabbitMQ. Сервіс складу реагує, а сервіс замовлень не знає про його існування. - **Спільні proto-пакети для контрактів.** Файли `.proto` слід зберігати у спільному репозиторії. І виробник, і споживач генерують типи з одного джерела істини — розходження стає помилкою CI, а не інцидентом у продакшені. Опції мікросервісів із контейнера DI у NestJS 11 роблять конфігурацію сервісу тестованою. Замість жорсткого прописування URL брокера в `main.ts` можна впровадити `ConfigService` і змінювати рядки зʼєднання для кожного середовища без перезбирання. ## Патерни надійності: тайм-аути, повторні спроби та запобіжники Внутрішні виклики gRPC без дедлайнів перетворюють одну повільну залежність на загальносистемний збій. Кожен виклик `ClientProxy.send()` має містити тайм-аут: ```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 }; } } ``` Повторні спроби слід застосовувати лише до ідемпотентних операцій. Повторення виклику `CreateOrder` ризикує створити дублікати замовлень. Повторення запиту `FindOne` безпечне, бо він не має побічних ефектів. Для функціональності запобіжника (circuit breaker) бібліотека [opossum](https://github.com/nodeshift/opossum) добре інтегрується з NestJS. Виклики `ClientGrpc` слід обгорнути в запобіжник, який відкривається після N послідовних збоїв і закривається після конфігурованого тайм-ауту скидання. > **Поширення trace ID у NestJS 11** > > NestJS 11 автоматично поширює trace ID через транспорти Kafka, RabbitMQ та gRPC. У поєднанні з [OpenTelemetry](https://opentelemetry.io/docs/languages/js/) це забезпечує повне розподілене трасування без власних перехоплювачів — значне покращення порівняно з NestJS 10, де було потрібне ручне поширення контексту. ## Питання співбесід щодо мікросервісів NestJS Технічні співбесіди на бекенд-позиції дедалі частіше перевіряють знання мікросервісів поряд зі специфічними для фреймворка патернами. Наведені питання охоплюють концепції, які найчастіше ставлять на співбесідах, орієнтованих на NestJS. **Яка різниця між `@MessagePattern` і `@EventPattern`?** `@MessagePattern` реалізує запит-відповідь: клієнт надсилає повідомлення й блокується, доки не отримає відповідь. `@EventPattern` реалізує модель fire-and-forget: клієнт випромінює подію й одразу продовжує виконання. Під капотом `@MessagePattern` повертає результат обробника через транспорт, тоді як `@EventPattern` відкидає повернуті значення. **Як NestJS завантажує та привʼязує файли `.proto` для gRPC?** NestJS використовує пакет `@grpc/proto-loader` для розбору файлів `.proto` під час запуску та генерації дескрипторів сервісів. Конфігурація `GrpcOptions` задає шлях до proto та назву пакета. Контролери, позначені `@GrpcMethod('ServiceName', 'MethodName')`, зіставляються з розібраними дескрипторами. Якщо метод, оголошений у proto, не має відповідного обробника, NestJS викидає помилку під час bootstrap. **Коли gRPC має замінити REST між мікросервісами?** gRPC підходить для внутрішніх викликів сервіс-сервіс, де важливі безпека типів, продуктивність і стрімінг. Protocol Buffers створюють менші корисні навантаження та швидшу серіалізацію, ніж JSON. Мультиплексування HTTP/2 зменшує накладні витрати на зʼєднання. REST залишається кращим для публічних API, браузерних клієнтів і сторонніх інтеграцій, де читабельність для людини та широка підтримка інструментів переважають над сирою продуктивністю. **Як працює патерн гібридного застосунку?** `NestFactory.create()` запускає HTTP-сервер. Виклик `app.connectMicroservice()` приєднує додаткові транспорти — gRPC, Kafka, Redis чи будь-який підтримуваний транспортер — до того самого екземпляра застосунку NestJS. Усі транспорти спільно використовують одне дерево модулів, контейнер DI та конвеєр middleware. `app.startAllMicroservices()` запускає всі підʼєднані транспортери, а `app.listen()` запускає рівень HTTP. **Які патерни надійності запобігають каскадним збоям?** Найважливіші три патерни: **тайм-аути** (кожен виклик RPC потребує дедлайну), **повторні спроби з джитером** (лише для ідемпотентних операцій, з експоненційним відкатом плюс випадковим джитером для уникнення ефекту натовпу) та **запобіжники** (відкриті після N збоїв, напіввідкриті після періоду охолодження, закриті після успішних перевірок працездатності). Інтеграція NestJS з RxJS робить тайм-аути та повторні спроби компонованими через оператори pipe. Для глибшої практики з патернами архітектури NestJS модуль [модулі та впровадження залежностей у NestJS](/technologies/node-nestjs/interview-questions/nestjs-modules-di) розглядає основи DI, що лежать в основі композиції мікросервісів. Модуль [middleware та перехоплювачі](/technologies/node-nestjs/interview-questions/middleware-interceptors) розглядає наскрізні питання, спільні для транспортів. ## Висновок - Транспортна абстракція NestJS 11 дозволяє сервісам перемикатися між TCP, gRPC, Kafka та NATS без переписування логіки обробників — декоратори `@MessagePattern` і `@EventPattern` працюють однаково в усіх транспортах - gRPC з Protocol Buffers забезпечує суворі контракти між сервісами через файли `.proto`, виявляючи помилки інтеграції на етапі компіляції, а не у продакшені - Гібридні застосунки поєднують HTTP і gRPC в одному екземплярі NestJS, усуваючи потребу розгортати окремі сервіси для публічних і внутрішніх інтерфейсів - Серверний стрімінг через Observable з RxJS уможливлює потоки даних у реальному часі без складності WebSocket — NestJS відображає кожне випромінення Observable на повідомлення потоку gRPC - Кожен внутрішній виклик RPC потребує дедлайну, повторні спроби застосовуються лише до ідемпотентних операцій, а запобіжники не дають одному повільному сервісу каскадно спричинити повний збій - Межі сервісів випливають із Domain-Driven Design: один відмежований контекст на сервіс, асинхронні події для комунікації між доменами, спільні proto-пакети для забезпечення контрактів --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/uk/blog/node-nestjs/nestjs-microservices-grpc-architecture