NestJS: criar e testar microservice TCP e gateway HTTP passo a passo
Por John Pires · 19 de março de 2026

NestJS: criar e testar microservice TCP e gateway HTTP passo a passo
Contexto: quando você vai usar isso
O problema que resolvemos
Microservices separam responsabilidades: um serviço calcula, outro expõe HTTP e autentica, outro persiste. No exemplo que acompanha este artigo vamos criar um microservice TCP responsável por lógica de cálculo (somar números) e um gateway HTTP que recebe requisições REST e delega essas operações pelo protocolo TCP.
Analogia simples
Imagine uma cozinha (microservice) que sabe cozinhar pratos e um garçom (gateway) que recebe pedidos do cliente. O garçom não cozinha — ele só traduz o pedido (HTTP) para a cozinha (TCP) e retorna o prato pronto. Essa separação é útil quando você quer escalar a cozinha de forma independente.
Este fluxo é comum em sistemas que exigem isolamento de responsabilidades, escalabilidade independente e comunicação eficiente dentro da mesma rede. Usaremos TCP porque é simples, eficiente e bem suportado pelo framework NestJS.
Explicação visual com diagramas mentais
Visão geral de alto nível
Antes de escrever uma linha de código, aprenda a imagem mental: cliente -> gateway HTTP -> microservice TCP -> gateway -> cliente. O gateway traduz HTTP/JSON para mensagens RPC sobre TCP com um padrão (cmd) e aguarda a resposta.
Fluxo de mensagens
Representação simples em ASCII (útil para memorizar):
Client (HTTP GET /math/sum?a=1&b=2)
|
v
Gateway HTTP (NestJS) --send({cmd: 'sum'}, [1,2])--> Microservice TCP (NestJS)
<-- response (3) -----------------------------------------
|
v
HTTP Response 200 {result: 3}
Essa imagem ajuda a lembrar que o gateway não chama funções locais; ele envia mensagens que o microservice interpreta com @MessagePattern.
Primeiro exemplo simples
Estrutura de projeto mínima
Vamos montar dois pequenos projetos: microservice (porta TCP 8877) e gateway HTTP (porta 3000). Não há banco, autenticação ou validação complexa — foco em comunicação. A estrutura mínima para cada projeto:
- microservice/
- src/main.ts
- src/app.module.ts
- src/math.controller.ts
- gateway/
- src/main.ts
- src/app.module.ts
- src/app.controller.ts
Microservice: primeira versão (errada comum)
Errado: assumir que controllers do Nest MVC funcionam do mesmo jeito em microservices, ou esquecer de usar @MessagePattern. Exemplo equivocado: tentar usar @Get em um microservice TCP. Isso não funciona porque o microservice não expõe HTTP.
// ERRADO (microservice/src/math.controller.ts) - não-funcional para TCP
import { Controller, Get } from '@nestjs/common';
@Controller()
export class MathController {
@Get('sum')
sum() {
return { result: 42 };
}
}
O código acima parece correto para um app HTTP, mas é inútil em um microservice que usa Transport.TCP. O Nest ignora rotas HTTP quando você cria a aplicação com createMicroservice.
Microservice: versão correta
Abaixo está a implementação funcional do microservice. Ele responde a padrões de mensagem com um objeto que descreve o comando. Observe o uso de @MessagePattern e a assinatura que recebe dados serializados.
// microservice/src/main.ts
import { NestFactory } from '@nestjs/core';
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.createMicroservice<MicroserviceOptions>(AppModule, {
transport: Transport.TCP,
options: { host: '127.0.0.1', port: 8877 },
});
await app.listen();
console.log('Microservice TCP rodando na porta 8877');
}
bootstrap();
O bootstrap acima cria um microservice TCP. Sem esse passo você não terá um servidor TCP escutando conexões. Agora o controller:
// microservice/src/math.controller.ts
import { Controller } from '@nestjs/common';
import { MessagePattern } from '@nestjs/microservices';
@Controller()
export class MathController {
@MessagePattern({ cmd: 'sum' })
sum(numbers: number[]): number {
// versão simples e síncrona: soma o array
return numbers.reduce((a, b) => a + b, 0);
}
}
Explicação: quando o microservice receber uma mensagem com padrão {cmd: 'sum'}, o método sum é invocado com os dados enviados (expect arrays de números).
Segundo exemplo mais realista
Gateway HTTP: implementação direta
A seguir implementamos o gateway HTTP que cria um ClientProxy para o microservice TCP. Aqui mostramos a forma correta de enviar a mensagem e aguardar a resposta usando rxjs firstValueFrom (em vez de toPromise que está depreciado).
// gateway/src/app.controller.ts
import { Controller, Get, Query } from '@nestjs/common';
import { ClientProxy, ClientProxyFactory, Transport } from '@nestjs/microservices';
import { firstValueFrom } from 'rxjs';
@Controller('math')
export class AppController {
private client: ClientProxy;
constructor() {
this.client = ClientProxyFactory.create({
transport: Transport.TCP,
options: { host: '127.0.0.1', port: 8877 },
});
}
@Get('sum')
async sum(@Query('a') a: string, @Query('b') b: string) {
const nums = [Number(a), Number(b)];
// envia a mensagem e aguarda a resposta do microservice
const result = await firstValueFrom(this.client.send<number>({ cmd: 'sum' }, nums));
return { result };
}
}
Contexto: o gateway recebe dois parâmetros de query, monta um array e delega a soma ao microservice via TCP. A resposta é enviada de volta como JSON.
Gateway: bootstrap e integração
Bootstrap do gateway é um app HTTP normal:
// gateway/src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
console.log('Gateway HTTP rodando na porta 3000');
}
bootstrap();
Depois de executar o microservice e o gateway, uma requisição GET http://localhost:3000/math/sum?a=2&b=3 deve responder { "result": 5 }.
Testes: como verificar sem ambos serviços no ar
Teste unitário do controller do gateway (errado x certo)
Errado: iniciar o microservice real durante testes unitários. Isso torna o teste lento e frágil. Em vez disso, deve-se mockar o ClientProxy.
// ERRADO: teste que tenta iniciar o microservice (não recomendado)
// ... (não colocamos código: esse é o antipadrão)
Certo: mockar o ClientProxy com um objeto que tenha send retornando um Observable. Abaixo um teste usando Jest que injeta um mockProvider.
// gateway/test/app.controller.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from '../src/app.controller';
import { of } from 'rxjs';
describe('AppController (unit)', () => {
let controller: AppController;
const mockClient = {
send: jest.fn().mockImplementation(() => of(5)),
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [AppController],
})
.overrideProvider('CLIENT_PROXY')
.useValue(mockClient)
.compile();
// instanciamos manualmente o controller com o mock
controller = new AppController();
// substituir client interno por mock
(controller as any).client = mockClient;
});
it('should return sum result from microservice', async () => {
const res = await controller.sum('2', '3');
expect(res).toEqual({ result: 5 });
expect(mockClient.send).toHaveBeenCalledWith({ cmd: 'sum' }, [2, 3]);
});
});
Explanação: o teste cria um stub para send retornando um observable com valor 5. Não precisamos do microservice real. Assim os testes ficam rápidos e determinísticos.
Teste e2e (opcional) com ambos serviços
Quando precisar de integração real, crie testes e2e que iniciem ambos os apps em portas temporárias e façam requisições HTTP ao gateway. Esses testes são úteis para validar contratos, porém devem rodar em pipelines específicos (integração) e não em todos os commits rápidos.
Tip: mantenha testes unitários rápidos e isole dependências externas com mocks; reserve testes de integração para pipelines separados.
Erros comuns de iniciante e como evitar
Esquecer await/firstValueFrom
Um erro frequente: chamar client.send e retornar o Observable sem converter/await. Isso gera respostas incompletas ou objetos Observable expostos para o cliente HTTP. Use firstValueFrom ou toPromise (com cuidado) para transformar Observable em Promise.
Antipadrão: acoplar lógica no gateway
Errado: copiar a lógica de negócio para o gateway porque "é mais rápido na implementação". Isso destrói a separação de responsabilidades. Mantenha o gateway fino: validações básicas, autenticação e encaminhamento. Lógica complexa deve ficar no microservice.
Portas, host e firewalls
Problemas de rede: microservice não escuta nas interfaces esperadas ou firewall bloqueando porta. Durante desenvolvimento use 127.0.0.1/localhost. Em produção, prefira networking controlado (VPN, VPC) e variáveis de ambiente para hospedar IP/porta.
Serialização e tipos
O Nest serializa/deserializa dados automaticamente via JSON. Garanta que os dados enviados sejam serializáveis. Evite enviar funções, classes complexas ou Buffers sem conversão apropriada.
- Não enviar objetos com métodos
- Enviar arrays e objetos simples
- Usar DTOs para documentar formato
Erros de exemplo: antes e depois (mini code review)
Antes: enviar Observable sem aguardar
Código que retorna o Observable diretamente do controller. Resultado: o Nest converte o Observable como resposta assíncrona e pode até funcionar, mas é frágil e confuso para iniciantes.
// ANTES (antipadrão)
@Get('sum')
sum(@Query('a') a, @Query('b') b) {
const nums = [Number(a), Number(b)];
return this.client.send<number>({ cmd: 'sum' }, nums); // retorna Observable
}
Embora Nest trate Observables em controllers HTTP, essa prática mistura paradigmas e dificulta tratamento de erros. Prefira converter explicitamente para Promise para controle fino.
Depois: aguardar com firstValueFrom
Correção clara, explícita e robusta:
// DEPOIS (recomendado)
@Get('sum')
async sum(@Query('a') a, @Query('b') b) {
const nums = [Number(a), Number(b)];
const result = await firstValueFrom(this.client.send<number>({ cmd: 'sum' }, nums));
return { result };
}
Esse padrão facilita captura de exceções com try/catch e integração com pipes/guards do Nest.
Conclusão prática
Resumo objetivo
Você aprendeu as diferenças entre uma aplicação HTTP e um microservice TCP no NestJS, viu o antipadrão (usar @Get no microservice, retornar Observable sem controle, iniciar microservices em testes unitários) e as correções recomendadas: usar @MessagePattern, firstValueFrom no gateway e mockar ClientProxy nos testes.
Próximos passos e checklist rápido
Checklist prático para aplicar em projetos reais:
- Separar responsabilidades: gateway fino, microservice com lógica.
- Usar Transport.TCP para comunicação simples e performática em rede privada.
- Converter Observables com firstValueFrom antes de retornar em endpoints HTTP.
- Mockar ClientProxy nos testes unitários; rodar testes de integração separadamente.
Aplicando essas práticas você terá serviços menores, testes rápidos e uma arquitetura mais previsível.
Resumo final: não copie padrões HTTP para microservices; entenda o papel de cada camada e teste isoladamente.
Se precisar, posso gerar um repositório base com scripts de start (npm scripts) e arquivos docker-compose para levantar gateway e microservice juntos, além de exemplos de testes e2e prontos para rodar.
Assuntos: NestJS, microservices, tcp, Testes, gateway
Leia também
Propagando contexto de requisição e trace IDs entre microservices com NestJS
Guia prático para propagar trace IDs em NestJS: middleware, AsyncLocalStorage, interceptors e wrappers de ClientProxy. Padrões, armadilhas e exemplos reais…
Rate limiting por usuário no NestJS com Guard + Redis (sliding window)
Aprenda a implementar rate limiting por usuário no NestJS usando Guard + Redis com sliding window. Guia passo a passo, exemplos práticos (Lua + ioredis)…
Testando Guards e Interceptors no NestJS com Jest e SuperTest — do jeito errado ao certo
Guards controlam acesso e Interceptors transformam respostas. Aprenda a testar ambos no NestJS: mostro o jeito errado, corrijo e explico testes unitários e…
Interceptor global de logs e tempo no NestJS: implementar e testar
Implemente um interceptor global no NestJS para logs e medição de tempo, registre-o no bootstrap e teste com Jest e supertest. Artigo prático passo a passo…
Implementando Circuit Breaker e Bulkhead em microservices NestJS com Cockatiel
Guia prático para integrar circuit breaker e bulkhead com Cockatiel em NestJS: módulo centralizado, wrapper Http resiliente, tratamento de erros e otimizações…
Autorização por permissões no NestJS usando decorators e guards
Aprenda a controlar autorização por permissões no NestJS usando decorators e guards. Tutorial prático, passo a passo, com exemplos funcionais, integração JWT…