Microsserviços com NestJS em 2026: Arquitetura gRPC, Padrões de Streaming e Perguntas de Entrevista

Guia prático sobre arquitetura de microsserviços NestJS com gRPC: camadas de transporte, Protocol Buffers, padrões de streaming e perguntas de entrevista para desenvolvedores backend em 2026.

Arquitetura de microsserviços NestJS com comunicação gRPC entre serviços backend distribuídos

Os microsserviços com NestJS se consolidaram como a abordagem padrão para construir backends Node.js distribuídos em 2026. Com a camada de transporte aprimorada do NestJS 12, suporte nativo a gRPC, filtros de exceção para chamadas RPC e propagação automática de identificadores de rastreamento, o framework oferece tudo o que é necessário para dividir um monolito em serviços bem delimitados sem sacrificar a experiência de desenvolvimento.

gRPC vs REST para serviços internos

gRPC utiliza Protocol Buffers e HTTP/2, proporcionando serialização até 10 vezes mais rápida e streaming bidirecional nativo em comparação com JSON sobre REST. Para comunicação entre serviços dentro de um cluster, gRPC reduz a latência e impõe contratos rigorosos por meio de arquivos .proto. REST continua sendo a melhor escolha para APIs públicas consumidas por navegadores e terceiros.

Arquitetura da camada de transporte em microsserviços NestJS

O pacote @nestjs/microservices substitui os controladores HTTP por handlers orientados a mensagens. Em vez de rotear por caminho de URL, cada handler responde a um padrão de mensagem, uma chave do tipo string ou objeto que identifica a operação. Essa abstração permite que a mesma lógica de handler funcione com TCP, Redis, NATS, Kafka, RabbitMQ ou gRPC sem alterações no código.

O NestJS 11 introduziu o método unwrap() em todos os transportadores, concedendo acesso direto à instância do cliente subjacente. Isso resolve um problema recorrente: inspecionar o estado da conexão, ajustar intervalos de keepalive ou acessar funcionalidades específicas do broker que a abstração do NestJS não expõe. O NestJS 12 complementa essa funcionalidade com hooks de pré-requisição, permitindo que os handlers executem lógica personalizada logo antes da invocação para preocupações transversais como verificações de autenticação ou métricas.

Dois padrões de comunicação definem os microsserviços NestJS:

  • Requisição-resposta (@MessagePattern): o cliente envia uma mensagem e aguarda uma resposta. Adequado para consultas síncronas como buscar um perfil de usuário ou validar um token.
  • Baseado em eventos (@EventPattern): o cliente emite um evento e segue sua execução, nenhuma resposta é esperada. Ideal para logs de auditoria, notificações ou acionamento de workflows subsequentes.
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);
  }
}

O handler @MessagePattern serializa automaticamente o valor de retorno e o envia de volta pelo transporte. O handler @EventPattern não retorna nada: o NestJS descarta qualquer valor de retorno.

Deprecação do EventPattern no NestJS 12

A partir do NestJS 12, os transportes TCP, Redis, NATS, MQTT e RabbitMQ exibem um aviso de deprecação quando um handler @EventPattern responde a ClientProxy#send(). Esse comportamento mudará na próxima versão maior, alinhando-se com a abordagem existente do Kafka. Recomenda-se migrar os handlers de eventos para semântica fire-and-forget real ou mudar para @MessagePattern para fluxos de requisição-resposta.

Configuração do gRPC como transporte de microsserviços NestJS

A integração do gRPC no NestJS começa com um arquivo .proto que define o contrato do serviço. Protocol Buffers impõe segurança de tipos no nível da rede: tanto o cliente quanto o servidor precisam concordar com o formato exato de cada mensagem antes que um único byte trafegue pela rede.

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;
}

No lado do servidor, a aplicação NestJS inicializa um microsserviço gRPC apontando para o arquivo proto e vinculando um endereço de rede:

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();

O controlador utiliza @GrpcMethod em vez de @MessagePattern. O decorator recebe o nome do serviço e o nome do método RPC como argumentos, mapeando diretamente para a definição 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);
  }
}

O NestJS gerencia automaticamente o carregamento dos arquivos proto, a serialização e o gerenciamento de conexões HTTP/2. O método do controlador recebe objetos TypeScript simples, nenhuma decodificação proto manual é necessária.

Padrões de streaming gRPC no NestJS

O streaming do servidor se encaixa em cenários onde uma única requisição produz múltiplos resultados ao longo do tempo: feeds de preços em tempo real, cursores de banco de dados paginados ou atualizações de progresso durante uma tarefa demorada. O handler retorna um Observable do RxJS, e o NestJS transmite cada valor emitido ao cliente como uma mensagem gRPC independente:

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,
    })),
  );
}

O streaming bidirecional utiliza @GrpcStreamCall(). Tanto o cliente quanto o servidor enviam fluxos independentes, útil para sistemas de chat, edição colaborativa ou ingestão de telemetria multiplexada. O handler recebe uma instância grpc.ServerDuplexStream e gerencia manualmente o ciclo de leitura/escrita.

O NestJS 12 adiciona o tratamento de contrapressão: os fluxos de requisição agora são pausados enquanto suas respostas aguardam para serem escritas. Isso previne o estouro de buffer quando um consumidor lento não consegue acompanhar um produtor rápido.

Tratamento de erros em streaming

Um Observable que falha dentro de um fluxo gRPC encerra a chamada inteira com status CANCELLED. A lógica de streaming precisa ser envolvida em operadores catchError para emitir metadados de erro apropriados ao gRPC. Erros de fluxo não tratados são a principal causa de desconexões inexplicáveis em serviços gRPC NestJS em produção.

Filtros de exceção gRPC no NestJS 12

O NestJS 12 introduz tratamento de exceção dedicado para gRPC, seguindo o padrão familiar de filtros de exceção HTTP. As classes de exceção específicas por status permitem que os handlers retornem códigos de erro nativos do gRPC em vez de falhas genéricas:

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',
    }));
  }
}

O filtro pode ser aplicado globalmente ou por controlador para garantir respostas de erro consistentes em todos os endpoints gRPC. Combinado com a classe RpcException existente, isso proporciona paridade no tratamento de erros entre os transportes HTTP e gRPC.

Aplicações híbridas: HTTP e gRPC no mesmo serviço

Muitos serviços NestJS em produção precisam tanto de uma API REST pública quanto de endpoints gRPC internos. O padrão de aplicação híbrida conecta múltiplos transportes a uma única instância 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();

Essa abordagem evita o deploy de serviços separados quando um único contexto delimitado necessita de interfaces externas e internas. O mesmo contêiner de injeção de dependências, os mesmos guards, interceptors e pipes se aplicam a ambos os transportes, reduzindo a duplicação e mantendo o comportamento consistente.

Pronto para mandar bem nas entrevistas de Node.js / NestJS?

Pratique com nossos simuladores interativos, flashcards e testes tecnicos.

Padrões Regexp do Kafka no NestJS 12

O NestJS 12 permite que @MessagePattern() e @EventPattern() aceitem expressões regulares para tópicos do Kafka. Um único handler pode agora se inscrever dinamicamente em múltiplos tópicos:

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);
  }
}

Isso reduz o código repetitivo quando múltiplos tópicos compartilham a mesma lógica de handler. A validação de chaves de padrão no NestJS 12 impõe um máximo de 256 caracteres: padrões mais longos falham silenciosamente e não correspondem a nenhum handler.

Migração para NATS v3 no NestJS 12

O NestJS 12 substitui o cliente NATS pelo pacote oficial @nats-io/transport-node. Esta é uma mudança importante: os deserializadores agora recebem o objeto de mensagem completo em vez do payload bruto.

typescript
// 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();
}

Todos os deserializadores NATS personalizados devem ser atualizados ao migrar para o NestJS 12. O novo cliente traz melhor desempenho e alinhamento com o ecossistema oficial do NATS.

Limites de serviço e design orientado a domínio com NestJS

Dividir um monolito em microsserviços sem limites claros cria um monolito distribuído, o pior das duas arquiteturas. Cada microsserviço NestJS deve possuir um único contexto delimitado: seu próprio banco de dados, seus próprios modelos de domínio e seu próprio ciclo de deploy.

Diretrizes práticas para definir os limites de serviço no NestJS:

  • Um módulo NestJS por raiz de agregado. Se Order e OrderLine sempre mudam juntos, pertencem ao mesmo serviço. Se User e Order mudam de forma independente, merecem serviços separados.
  • Eventos assíncronos para efeitos colaterais entre domínios. Quando um pedido é realizado, um evento order.created é emitido via Kafka ou RabbitMQ. O serviço de estoque reage sem que o serviço de pedidos saiba de sua existência.
  • Pacotes proto compartilhados para contratos. Os arquivos .proto ficam armazenados em um repositório compartilhado. Tanto o produtor quanto o consumidor geram tipos a partir da mesma fonte de verdade, a divergência se torna uma falha de CI, não um incidente em produção.

As opções de microsserviços do NestJS 12 a partir do contêiner DI tornam a configuração dos serviços testável. Em vez de codificar as URLs dos brokers diretamente no main.ts, basta injetar um ConfigService e trocar as strings de conexão por ambiente sem necessidade de recompilar.

Padrões de confiabilidade: Timeouts, Retentativas e Circuit Breakers

Chamadas gRPC internas sem deadlines transformam uma única dependência lenta em uma falha generalizada do sistema. Cada chamada ClientProxy.send() deve incluir um timeout:

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 };
  }
}

As retentativas devem ser aplicadas apenas a operações idempotentes. Retentar uma chamada CreateOrder pode gerar pedidos duplicados. Retentar uma consulta FindOne é seguro porque não possui efeitos colaterais.

Para a funcionalidade de circuit breaker, a biblioteca opossum se integra bem com o NestJS. As chamadas ClientGrpc podem ser envolvidas em um circuit breaker que abre após N falhas consecutivas e fecha após um timeout de reset configurável.

Propagação de rastreamento no NestJS 12

O NestJS 12 continua a propagação automática de identificadores de rastreamento introduzida no NestJS 11 através dos transportes Kafka, RabbitMQ e gRPC. Combinado com o OpenTelemetry, isso permite rastreamento distribuído completo sem interceptors personalizados.

Perguntas de entrevista sobre microsserviços NestJS

As entrevistas técnicas para posições backend avaliam cada vez mais o conhecimento em microsserviços junto com padrões específicos do framework. Estas perguntas cobrem os conceitos mais frequentemente abordados em entrevistas focadas em NestJS.

Qual é a diferença entre @MessagePattern e @EventPattern?

@MessagePattern implementa o padrão requisição-resposta: o cliente envia uma mensagem e bloqueia até receber uma resposta. @EventPattern implementa o padrão fire-and-forget: o cliente emite um evento e continua a execução imediatamente. Internamente, @MessagePattern retorna o resultado do handler pelo transporte, enquanto @EventPattern descarta os valores de retorno. O NestJS 12 deprecou a resposta a partir de handlers @EventPattern na maioria dos transportes.

Como o NestJS carrega e vincula os arquivos .proto para gRPC?

O NestJS utiliza o pacote @grpc/proto-loader para fazer o parse dos arquivos .proto na inicialização e gerar os descritores de serviço. A configuração GrpcOptions especifica o caminho do proto e o nome do pacote. Os controladores anotados com @GrpcMethod('ServiceName', 'MethodName') são associados aos descritores parseados. Se um método declarado no proto não possui um handler correspondente, o NestJS lança um erro na inicialização.

Quando o gRPC deve substituir o REST entre microsserviços?

O gRPC é adequado para chamadas internas entre serviços onde a segurança de tipos, o desempenho e o streaming são importantes. Protocol Buffers produz payloads menores e serialização mais rápida que JSON. O multiplexamento HTTP/2 reduz a sobrecarga de conexões. REST continua sendo preferível para APIs públicas, clientes de navegador e integrações de terceiros onde a legibilidade humana e o amplo suporte de ferramentas superam o desempenho bruto.

Como funciona o padrão de aplicação híbrida?

NestFactory.create() inicializa um servidor HTTP. A chamada a app.connectMicroservice() vincula transportes adicionais, gRPC, Kafka, Redis ou qualquer transportador suportado, à mesma instância NestJS. Todos os transportes compartilham a mesma árvore de módulos, o mesmo contêiner DI e o mesmo pipeline de middleware. app.startAllMicroservices() inicia todos os transportadores conectados, e app.listen() inicia a camada HTTP.

Quais padrões de confiabilidade previnem falhas em cascata?

Três padrões são fundamentais: timeouts (cada chamada RPC precisa de um deadline), retentativas com jitter (apenas para operações idempotentes, com backoff exponencial mais jitter aleatório para evitar efeitos de manada), e circuit breakers (abertos após N falhas, semi-abertos após um período de resfriamento, fechados assim que as verificações de saúde são bem-sucedidas). A integração RxJS do NestJS torna os timeouts e retentativas combináveis por meio de operadores pipe.

O que mudou para microsserviços no NestJS 12?

O NestJS 12 adicionou filtros de exceção gRPC para tratamento de erros específico por status, padrões regexp do Kafka para handlers multi-tópico, suporte NATS v3 com um novo pacote cliente, hooks de pré-requisição para handlers de microsserviços, e melhor tratamento de contrapressão para streams gRPC. O aviso de deprecação do @EventPattern sinaliza uma mudança de comportamento futura onde os handlers de eventos não responderão mais a chamadas send().

Para aprofundar os padrões de arquitetura NestJS, o módulo de módulos e injeção de dependências do NestJS cobre os fundamentos de DI que sustentam a composição de microsserviços. O módulo de middleware e interceptors aborda as preocupações transversais compartilhadas entre transportes. O guia de NestJS e GraphQL apresenta uma alternativa ao gRPC para APIs de serviços tipados.

Comece a praticar!

Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.

Fontes

Pontos-chave para microsserviços NestJS em 2026

  • A abstração de transporte do NestJS 12 permite que os serviços alternem entre TCP, gRPC, Kafka e NATS sem reescrever a lógica dos handlers: os decorators @MessagePattern e @EventPattern funcionam de maneira idêntica em todos os transportes
  • gRPC com Protocol Buffers impõe contratos rigorosos entre serviços por meio de arquivos .proto, detectando erros de integração em tempo de compilação em vez de em produção
  • As aplicações híbridas combinam HTTP e gRPC na mesma instância NestJS, eliminando a necessidade de fazer deploy de serviços separados para interfaces públicas e internas
  • O streaming do servidor via Observables do RxJS permite fluxos de dados em tempo real sem a complexidade de WebSockets, e o NestJS 12 adiciona tratamento de contrapressão para consumidores lentos
  • Cada chamada RPC interna requer um deadline, as retentativas se aplicam apenas a operações idempotentes, e os circuit breakers impedem que um único serviço lento provoque uma falha em cascata em todo o sistema
  • Os limites de serviço seguem o design orientado a domínio: um contexto delimitado por serviço, eventos assíncronos para comunicação entre domínios, pacotes proto compartilhados para aplicação de contratos

Comece a praticar!

Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.

Desafio do dia

Você saberia encontrar o bug em Node.js / NestJS?

Um trecho real, um bug escondido, uma tentativa por dia. Sem conta para testar.

Anthony Fillion-Maillet

Escrito por

Anthony Fillion-Maillet

Fundador da SharpSkill

Desenvolvedor fullstack há mais de 10 anos. Dirige a SharpSkill e responde por tudo o que é publicado aqui.

Atualizado em 22 de outubro de 2026

Tags

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

Compartilhar

Artigos relacionados