Direct Exchange
Routing key: Exact string match between message routing key and queue binding key
A direct exchange routes messages to queues whose binding key exactly matches the message's routing key. If routing key = 'order.created', only queues bound with 'order.created' receive the message. The default exchange (empty name) is a pre-declared direct exchange that routes by queue name.
How it works
Direct exchanges implement point-to-point routing. The routing decision is a simple string equality check: message routing key == binding key.
Default exchange: RabbitMQ pre-declares a nameless direct exchange (''). When you publish to the default exchange with routing key = queue name, the message is delivered directly to that queue. This lets you publish to a queue directly without explicit exchange/binding setup.
Use for task queues: Workers compete to consume from the same queue. Publish all tasks to the default exchange with the queue name as routing key. Multiple consumers share the load round-robin.
Multiple bindings: Multiple queues can be bound to a direct exchange with the same routing key. In that case, the message is delivered to all bound queues – equivalent to a multicast. This is a less common pattern; use fanout for intentional broadcasts.
Direct exchange vs nameless exchange: They behave identically – the nameless exchange is a direct exchange. The explicit named direct exchange is useful when you want a clear separation between routing logic and queue names.
Use Cases
- →
Task queues – worker processes competing to consume jobs
- →
Routing to specific services by operation name
- →
Default exchange for simple queue publish-subscribe
- →
Priority routing with separate queues per priority level
Examples
// Producer: publish to default exchange, routing key = queue name
ch.publish('', 'task_queue',
Buffer.from('{"task": "resize_image", "url": "..."}'),
{ persistent: true }
);
// Multiple competing workers consume from same queue
ch.consume('task_queue', async (msg) => {
await processTask(JSON.parse(msg.content.toString()));
ch.ack(msg);
}, { noAck: false });await ch.assertExchange('events', 'direct', { durable: true });
// Bind different queues to same exchange with different routing keys
await ch.assertQueue('payments.created', { durable: true });
await ch.bindQueue('payments.created', 'events', 'payment.created');
await ch.assertQueue('payments.failed', { durable: true });
await ch.bindQueue('payments.failed', 'events', 'payment.failed');
// Publisher uses routing key to select target queue
ch.publish('events', 'payment.created', payload);