Step-by-step tutorials from Hello World to topic exchanges. The single best RabbitMQ learning resource. Read →
When a user signs up, you need to send a welcome email, resize their photo, notify the CRM, and update analytics. Doing all this synchronously makes signup slow and brittle. RabbitMQ decouples operations: your API publishes a message and returns immediately. Workers process jobs asynchronously, independently, and reliably.
API handler = producer. Background service = consumer. RabbitMQ = the reliable broker between them that persists messages, routes to the right queues, and acknowledges completion.
| Concept | What it is |
|---|---|
| Exchange | Routing engine — receives messages and routes them to queues based on rules |
| Queue | Buffer that stores messages until a consumer picks them up |
| Binding | Rule connecting an exchange to a queue (with optional routing key) |
| Acknowledgement (ACK) | Consumer signals "I processed this successfully" — message deleted from queue |
| NACK | Consumer signals "I failed" — message can be requeued or sent to dead letter |
| DLX (Dead Letter Exchange) | Where failed messages go for inspection and retry |
| Exchange | Routing | Use case |
|---|---|---|
| direct | Exact routing key match | Route to specific queue: "email", "resize" |
| fanout | Broadcast to all bound queues | Notify ALL consumers: "user.created" |
| topic | Wildcard matching (order.*, *.error) | Flexible routing: "order.placed", "order.shipped" |
npm install amqplib
import amqp from 'amqplib';
// ── Producer ─────────────────────────────────────────────────────
const conn = await amqp.connect(process.env.RABBITMQ_URL);
const ch = await conn.createChannel();
await ch.assertQueue('emails', {
durable: true,
arguments: { 'x-dead-letter-exchange': 'dlx' }
});
ch.sendToQueue('emails',
Buffer.from(JSON.stringify({ to: 'user@example.com', template: 'welcome', userId: 123 })),
{ persistent: true } // Survive RabbitMQ restart
);
// ── Consumer ─────────────────────────────────────────────────────
await ch.assertQueue('emails', { durable: true });
ch.prefetch(1); // Process one message at a time
ch.consume('emails', async (msg) => {
if (!msg) return;
try {
const job = JSON.parse(msg.content.toString());
await sendEmail(job.to, job.template);
ch.ack(msg); // Success — remove from queue
} catch (err) {
ch.nack(msg, false, false); // Failure — send to dead letter
}
});
// Set up Dead Letter Exchange + Queue
await ch.assertExchange('dlx', 'direct', { durable: true });
await ch.assertQueue('emails.failed', { durable: true });
await ch.bindQueue('emails.failed', 'dlx', 'emails');
// Failed messages now accumulate in emails.failed for inspection
Access RabbitMQ's management dashboard at http://localhost:15672 (user/password in dev). View queues, message rates, consumer count, and manually requeue failed messages — invaluable for debugging.