NestJS combined with RabbitMQ is the golden standard for building scalable, decoupled, event-driven microservices. When modular codebases outgrow standard REST endpoints, an asynchronous message broker becomes essential.
๐ฏ The Architecture
Traditional monolithic backends handle database reads, authentication, image processing, and payment webhooks on a single server. A sudden spike in payment webhooks can crash the entire system. By decoupling workloads into independent microservices communicating through RabbitMQ, we ensure high system resilience and parallelized message queue processing.
๐ ๏ธ Step-by-Step Implementation
1. Initializing the Microservice
In NestJS, setting up a microservice listener requires changing our main.ts file bootstrap process:
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.RMQ,
options: {
urls: [process.env.RABBITMQ_URL || 'amqp://localhost:5672'],
queue: 'billing_queue',
queueOptions: {
durable: true,
},
},
});
await app.listen();
}
bootstrap();2. Message Handling in Controllers
Using @EventPattern and @MessagePattern, we can consume events and messages asynchronously:
import { Controller } from '@nestjs/common';
import { EventPattern, Payload, Ctx, RmqContext } from '@nestjs/microservices';
@Controller()
export class BillingController {
@EventPattern('payment_created')
async handlePaymentCreated(@Payload() data: any, @Ctx() context: RmqContext) {
const channel = context.getChannelRef();
const originalMsg = context.getMessage();
try {
console.log('Processing payment invoice for: ', data.orderId);
// Process business logic here...
channel.ack(originalMsg); // Manual acknowledgment
} catch (err) {
// Handle retry queues / DLX (Dead Letter Exchange)
}
}
}๐ Key Takeaways
- โธDurable Queues: Keep
durable: trueso that even if RabbitMQ crashes, messages are saved on disk and not lost. - โธManual Acknowledgment: Disable auto-ACK and trigger acknowledgments manually. This ensures that if a microservice crashes mid-process, RabbitMQ puts the message back in queue to avoid data loss.