Skip to main content
fanout

Fanout Exchange

Routing key: Ignored – message delivered to ALL bound queues regardless of routing key

A fanout exchange broadcasts every received message to all queues bound to it. The routing key is completely ignored. Every bound queue gets a copy of the message. Use fanout for broadcast patterns: cache invalidation across services, event notifications to multiple subscribers, logging fanout to multiple sinks.

How it works

Fanout exchanges implement the broadcast pattern. Every message is copied and delivered to every queue that has a binding to the exchange, regardless of routing key.

RoutingKey ignored: publishing with routing key 'anything' or '' delivers identically to all bound queues. This is by design – fanout is purely for broadcast.

Copy per binding: each bound queue receives an independent copy of the message. If queue A and queue B are both bound, both receive the message. Consumers on each queue process independently.

Dynamic subscriptions: services can bind/unbind to a fanout exchange at runtime without coordinating with the publisher. A new service subscribing to 'user.events' fanout automatically receives all future user events without any publisher change.

Common pattern – exclusive queues per consumer: Each consumer creates an exclusive, auto-delete queue and binds it to the fanout exchange. When the consumer disconnects, the queue is deleted. This gives each consumer its own private event stream from the same broadcast.

Performance: fanout exchanges are the fastest exchange type because no routing key comparison is needed.

Use Cases

  • Cache invalidation – notify all application servers to invalidate a cache entry

  • Event broadcasting – user.created delivered to email service, analytics service, audit service simultaneously

  • Live feed updates – send the same update to all connected clients

  • Pub/sub with dynamic subscribers that join/leave at runtime

Examples

Broadcast to all bound queues
await ch.assertExchange('user.events', 'fanout', { durable: true });

// Each service creates its own exclusive queue and binds
// Service A: email notifications
const q1 = await ch.assertQueue('', { exclusive: true });
await ch.bindQueue(q1.queue, 'user.events', '');

// Service B: analytics
const q2 = await ch.assertQueue('', { exclusive: true });
await ch.bindQueue(q2.queue, 'user.events', '');

// Publisher sends once; both services receive
ch.publish('user.events', '',  // routing key ignored
  Buffer.from(JSON.stringify({ userId: 'usr_1', event: 'signup' }))
);

See Also