Мікросервіси на NestJS у 2026: архітектура, gRPC та питання співбесід

Практичний посібник з архітектури мікросервісів NestJS на gRPC: межі сервісів, транспортні рівні, патерни стрімінгу та поширені питання співбесід на 2026 рік.

Архітектура мікросервісів NestJS з комунікацією gRPC між розподіленими бекенд-сервісами

Мікросервіси NestJS стали стандартним підходом до побудови розподілених бекендів на Node.js у 2026 році. Завдяки покращеному рівню транспортерів у NestJS 12, першокласній підтримці gRPC, фільтрам винятків для RPC-викликів та автоматичному поширенню 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 12 розширює це хуками перед запитом, що дозволяють обробникам виконувати власну логіку безпосередньо перед викликом для наскрізних завдань на кшталт перевірки авторизації чи метрик.

Два патерни комунікації керують мікросервісами NestJS:

  • Запит-відповідь (@MessagePattern): клієнт надсилає повідомлення й чекає на відповідь. Підходить для синхронних запитів на кшталт отримання профілю користувача чи перевірки токена.
  • На основі подій (@EventPattern): клієнт випромінює подію й рухається далі, відповіді не очікується. Ідеально для журналів аудиту, сповіщень чи запуску процесів на стороні споживача.
orders.controller.tstypescript
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 відкидає будь-яке повернуте значення.

Застаріння EventPattern у NestJS 12

Починаючи з NestJS 12, транспорти TCP, Redis, NATS, MQTT і RabbitMQ записують попередження про застаріння, коли обробник @EventPattern відповідає на ClientProxy#send(). Ця поведінка зміниться в наступній мажорній версії, узгоджуючись із наявним підходом Kafka. Слід мігрувати обробники подій до справжньої семантики fire-and-forget або перейти на @MessagePattern для потоків запит-відповідь.

Налаштування gRPC як транспорту мікросервісу NestJS

Інтеграція gRPC у NestJS починається з файлу .proto, який визначає контракт сервісу. Protocol Buffers забезпечують безпеку типів на рівні мережі, і клієнт, і сервер мають узгодити точну форму кожного повідомлення, перш ніж бодай один байт пройде мережею.

proto/users.protoprotobuf
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 та привʼязуючи мережеву адресу:

main.tstypescript
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();

Контролер використовує @GrpcMethod замість @MessagePattern. Декоратор приймає назву сервісу та назву методу RPC як аргументи, відображаючи їх безпосередньо на визначення proto:

users.controller.tstypescript
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:

users.controller.ts (server streaming)typescript
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,
    })),
  );
}

Двонаправлений стрімінг використовує @GrpcStreamCall(). І клієнт, і сервер надсилають незалежні потоки, корисно для чат-систем, спільного редагування чи мультиплексованого збору телеметрії. Обробник отримує екземпляр grpc.ServerDuplexStream і керує циклом читання/запису вручну.

NestJS 12 додає обробку зворотного тиску (backpressure): потоки запитів призупиняються, поки їхні відповіді очікують на запис. Це запобігає переповненню буфера, коли повільний споживач не встигає за швидким виробником.

Обробка помилок у стрімінгу

Observable, який аварійно завершився всередині потоку gRPC, припиняє весь виклик зі статусом CANCELLED. Логіку потоку слід обгортати операторами catchError і випромінювати відповідні для gRPC метадані помилки. Необроблені помилки потоку — причина номер один загадкових розривів зʼєднання у продакшн-сервісах NestJS gRPC.

Фільтри винятків gRPC у NestJS 12

NestJS 12 запроваджує спеціалізовану обробку винятків для gRPC, відображаючи знайомий патерн фільтрів винятків HTTP. Класи винятків, специфічні для статусу, дозволяють обробникам повертати нативні для gRPC коди помилок замість загальних збоїв:

grpc-exception.filter.tstypescript
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',
    }));
  }
}

Фільтр можна застосувати глобально або на рівні контролера для забезпечення узгоджених відповідей про помилки в усіх ендпойнтах gRPC. У поєднанні з наявним класом RpcException це забезпечує паритет обробки помилок між транспортами HTTP і gRPC.

Гібридні застосунки: HTTP і gRPC в одному сервісі

Багато реальних сервісів NestJS потребують як публічного REST API, так і внутрішніх ендпойнтів gRPC. Патерн гібридного застосунку підʼєднує кілька транспортів до одного екземпляра NestJS:

main.ts (hybrid application)typescript
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();

Цей підхід дозволяє уникнути розгортання окремих сервісів, коли один відмежований контекст потребує і зовнішнього, і внутрішнього інтерфейсів. Той самий контейнер впровадження залежностей, ґарди, перехоплювачі та пайпи застосовуються до обох транспортів, це зменшує дублювання й зберігає узгодженість поведінки.

Готовий до співбесід з Node.js / NestJS?

Практикуйся з нашими інтерактивними симуляторами, flashcards та технічними тестами.

Патерни regexp для Kafka у NestJS 12

NestJS 12 дозволяє @MessagePattern() та @EventPattern() приймати регулярні вирази для тем Kafka. Один обробник тепер може динамічно підписуватися на кілька тем:

multi-topic.controller.tstypescript
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);
  }
}

Це зменшує шаблонний код, коли кілька тем використовують ту саму логіку обробника. Валідація ключа патерну в NestJS 12 обмежує максимум 256 символами: довші патерни мовчки провалюються і не відповідають жодному обробнику.

Міграція NATS v3 у NestJS 12

NestJS 12 замінює клієнт NATS офіційним пакетом @nats-io/transport-node. Це зламна зміна: десеріалізатори тепер отримують повний обʼєкт повідомлення замість сирого навантаження.

typescript
// До (NestJS 11)
deserialize(value: any): any {
  return JSON.parse(value);
}

// Після (NestJS 12): читання навантаження через msg.json()
deserialize(msg: any): any {
  return msg.json();
}

При міграції на NestJS 12 слід оновити всі власні десеріалізатори NATS. Новий клієнт забезпечує покращену продуктивність і узгодженість з офіційною екосистемою NATS.

Межі сервісів і Domain-Driven Design у NestJS

Розбиття моноліту на мікросервіси без чітких меж створює розподілений моноліт, найгірше з обох архітектур. Кожен мікросервіс NestJS має володіти єдиним відмежованим контекстом: власною базою даних, власними доменними моделями та власним життєвим циклом розгортання.

Практичні рекомендації щодо проведення меж сервісів у NestJS:

  • Один модуль NestJS на корінь агрегату. Якщо Order і OrderLine завжди змінюються разом, вони належать до одного сервісу. Якщо User і Order змінюються незалежно, вони заслуговують на окремі сервіси.
  • Асинхронні події для побічних ефектів між доменами. Коли оформлено замовлення, слід випромінити подію order.created через Kafka чи RabbitMQ. Сервіс складу реагує, а сервіс замовлень не знає про його існування.
  • Спільні proto-пакети для контрактів. Файли .proto слід зберігати у спільному репозиторії. І виробник, і споживач генерують типи з одного джерела істини, розходження стає помилкою CI, а не інцидентом у продакшені.

Опції мікросервісів із контейнера DI у NestJS 12 роблять конфігурацію сервісу тестованою. Замість жорсткого прописування URL брокера в main.ts можна впровадити ConfigService і змінювати рядки зʼєднання для кожного середовища без перезбирання.

Патерни надійності: тайм-аути, повторні спроби та запобіжники

Внутрішні виклики gRPC без дедлайнів перетворюють одну повільну залежність на загальносистемний збій. Кожен виклик ClientProxy.send() має містити тайм-аут:

orders.service.tstypescript
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 добре інтегрується з NestJS. Виклики ClientGrpc слід обгорнути в запобіжник, який відкривається після N послідовних збоїв і закривається після конфігурованого тайм-ауту скидання.

Поширення trace ID у NestJS 12

NestJS 12 продовжує автоматичне поширення trace ID, запроваджене в NestJS 11, через транспорти Kafka, RabbitMQ та gRPC. У поєднанні з OpenTelemetry це забезпечує повне розподілене трасування без власних перехоплювачів.

Питання співбесід щодо мікросервісів NestJS

Технічні співбесіди на бекенд-позиції дедалі частіше перевіряють знання мікросервісів поряд зі специфічними для фреймворка патернами. Наведені питання охоплюють концепції, які найчастіше ставлять на співбесідах, орієнтованих на NestJS.

Яка різниця між @MessagePattern і @EventPattern?

@MessagePattern реалізує запит-відповідь: клієнт надсилає повідомлення й блокується, доки не отримає відповідь. @EventPattern реалізує модель fire-and-forget: клієнт випромінює подію й одразу продовжує виконання. Під капотом @MessagePattern повертає результат обробника через транспорт, тоді як @EventPattern відкидає повернуті значення. NestJS 12 позначив як застарілу відповідь з обробників @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 12?

NestJS 12 додав фільтри винятків gRPC для обробки помилок, специфічних для статусу, патерни regexp для Kafka для обробки кількох тем, підтримку NATS v3 з новим пакетом клієнта, хуки перед запитом для обробників мікросервісів та покращене керування зворотним тиском для потоків gRPC. Попередження про застаріння @EventPattern сигналізує про майбутню зміну поведінки, коли обробники подій більше не відповідатимуть на виклики send().

Для глибшої практики з патернами архітектури NestJS модуль модулі та впровадження залежностей у NestJS розглядає основи DI, що лежать в основі композиції мікросервісів. Модуль middleware та перехоплювачі розглядає наскрізні питання, спільні для транспортів. Посібник NestJS і GraphQL описує альтернативу gRPC для типізованих API сервісів.

Починай практикувати!

Перевір свої знання з нашими симуляторами співбесід та технічними тестами.

Джерела

  • NestJS v12 is Now Available — офіційне оголошення, що охоплює фільтри винятків gRPC, патерни regexp для Kafka, NATS v3, хуки перед запитом
  • NestJS Releases — журнал змін, включно зі зворотним тиском для потоків gRPC, обмеженнями ключів патернів, застарінням EventPattern
  • NestJS Hybrid Application Documentation — офіційний посібник з поєднання транспортів HTTP та мікросервісів

Ключові висновки щодо мікросервісів NestJS у 2026

  • Транспортна абстракція NestJS 12 дозволяє сервісам перемикатися між TCP, gRPC, Kafka та NATS без переписування логіки обробників: декоратори @MessagePattern і @EventPattern працюють однаково в усіх транспортах
  • gRPC з Protocol Buffers забезпечує суворі контракти між сервісами через файли .proto, виявляючи помилки інтеграції на етапі компіляції, а не у продакшені
  • Гібридні застосунки поєднують HTTP і gRPC в одному екземплярі NestJS, усуваючи потребу розгортати окремі сервіси для публічних і внутрішніх інтерфейсів
  • Серверний стрімінг через Observable з RxJS уможливлює потоки даних у реальному часі без складності WebSocket, а NestJS 12 додає обробку зворотного тиску для повільних споживачів
  • Кожен внутрішній виклик RPC потребує дедлайну, повторні спроби застосовуються лише до ідемпотентних операцій, а запобіжники не дають одному повільному сервісу каскадно спричинити повний збій
  • Межі сервісів випливають із Domain-Driven Design: один відмежований контекст на сервіс, асинхронні події для комунікації між доменами, спільні proto-пакети для забезпечення контрактів

Починай практикувати!

Перевір свої знання з нашими симуляторами співбесід та технічними тестами.

Щоденний виклик

Чи знайдеш ти помилку в Node.js / NestJS?

Справжній фрагмент коду, прихована помилка, одна спроба на день. Щоб спробувати, акаунт не потрібен.

Anthony Fillion-Maillet

Автор:

Anthony Fillion-Maillet

Засновник SharpSkill

Fullstack-розробник понад 10 років. Керує SharpSkill і відповідає за все, що тут публікується.

Оновлено 22 жовтня 2026 р.

Теги

#nestjs
#microservices
#grpc
#nodejs
#typescript
#architecture

Поділитися

Пов'язані статті