Connect Nest Services Without HTTP
Microservices — TCP, Redis, NATS
Nest supports several transports for service-to-service communication via `@nestjs/microservices`. Same code, different wire format.
What you'll learn
- Create a microservice with NestFactory.createMicroservice
- Send messages with ClientProxy
- Pick a transport — TCP, Redis, NATS, RabbitMQ
When one Nest service needs to call another, you can use HTTP — but
@nestjs/microservices offers transports that are faster, simpler, or
better suited to async messaging. The code looks almost identical to a
regular controller; only the bootstrap and the client change.
Install
npm i @nestjs/microservices A Microservice Server
Instead of NestFactory.create, use createMicroservice and pick a
transport. TCP is the simplest — no broker required.
import { NestFactory } from '@nestjs/core';
import { Transport, MicroserviceOptions } from '@nestjs/microservices';
async function bootstrap() {
const app = await NestFactory.createMicroservice<MicroserviceOptions>(
AppModule,
{
transport: Transport.TCP,
options: { host: '0.0.0.0', port: 4000 },
},
);
await app.listen();
}
bootstrap(); Inside the module, handlers are decorated with @MessagePattern (for
request/response) or @EventPattern (for fire-and-forget).
import { Controller } from '@nestjs/common';
import { MessagePattern, EventPattern, Payload } from '@nestjs/microservices';
@Controller()
export class UsersMessageController {
@MessagePattern('user.findOne')
findOne(@Payload() id: number) {
return this.users.findById(id);
}
@EventPattern('user.created')
onCreated(@Payload() user: { id: number; email: string }) {
// side effects — send welcome email, etc.
}
} The difference: @MessagePattern expects a response, @EventPattern
does not. Use events for “this happened” notifications.
The Client Side
The calling service registers a ClientsModule and injects a
ClientProxy.
@Module({
imports: [
ClientsModule.register([
{
name: 'USERS_SERVICE',
transport: Transport.TCP,
options: { host: 'users', port: 4000 },
},
]),
],
})
export class GatewayModule {}
@Injectable()
export class GatewayService {
constructor(@Inject('USERS_SERVICE') private readonly client: ClientProxy) {}
findUser(id: number) {
return firstValueFrom(this.client.send('user.findOne', id));
}
notify(user: { id: number; email: string }) {
this.client.emit('user.created', user);
}
} send returns an Observable; wrap it with firstValueFrom (from
rxjs) when you want a Promise. emit is fire-and-forget — no
response to wait for.
Picking a Transport
- TCP — simplest, no dependencies. Good for two services in the same cluster.
- Redis — pub/sub via Redis. Easy if you already run Redis. Great for events.
- NATS — purpose-built messaging. Fast, lightweight, supports pub/sub and request/reply.
- RabbitMQ / Kafka — full message brokers. Durable queues, work distribution, replay. Pick these when you need real reliability guarantees.
The handler code doesn’t change between transports — only the bootstrap and the client config. Start with TCP, swap when you outgrow it.
gRPC Microservices →