Skip to main content

On This Page

RabbitMQ Topic Routing Demystified: Event Names vs Routing Keys

3 min read
Share

These articles are AI-generated summaries. Please check the original sources for full details.

RabbitMQ Topic Routing: Event Names vs Routing Keys

RabbitMQ topic routing is powerful yet easily misunderstood when developers confuse binding patterns with publish routing keys. A concrete event name like ‘order.created’ should never be published as ‘order.*‘—that would disable wildcard matching entirely.

Why This Matters

In production systems, mixing up event names and binding patterns leads to silent message loss or unintended consumers receiving events they should not handle. For example, publishing with a literal '' character instead of a concrete key means no queue bound to ‘order.’ will ever match because ’*’ is treated as an ordinary string symbol rather than a wildcard.

Many teams rely on intuitive assumptions that fail under load—especially when multiple microservices share a topic exchange without explicit naming conventions for routing keys versus bindings. Libraries like Rabbit Relay enforce this distinction at compile time by treating configured ‘routingKey’ values as concrete strings while reserving patterns for queue bindings only.

Key Insights

    • Binding pattern # matches zero or more words; * matches exactly one word (RabbitMQ documentation standard).
    • Common mistake: publishing messages with order.* as the actual routing key string—this disables all wildcard logic because * becomes literal.
    • In most designs the event name equals the publish routing key (e.g., order.created), while queues bind using patterns like order.*.
    • Rabbit Relay’s default behavior uses the event name as the concrete publish key; overrides require explicit routingKey option in produce() call.

Working Examples

Publisher example producing an OrderCreated event; default routing key equals event name.

import { RabbitMQBroker, event } from "@bitspacerlabs/rabbit-relay";
type OrderCreated = {
  orderId: string;
  amount: number;
};
const orderCreated = event("order.created", "v1").of<OrderCreated>();
const broker = new RabbitMQBroker("orders.publisher");
const pub = await broker
  .queue("orders.publisher.q")
  .exchange("orders.ex", {
    exchangeType: "topic",
    publisherConfirms: true,
});
await pub.produce(
  orderCreated({
    orderId:	"o-1",
    amount:	42,
})
);
await broker.close();

Consumer queue bound to exchange with pattern ‘order.*’; only messages whose concrete keys match receive them.

import { RabbitMQBroker, type EventEnvelope } from "@bitspacerlabs/rabbit-relay";
type OrderCreated = {
  orderId:	string;
  amount:	number;
};
type Contracts = {
	"order.created": EventEnvelope<OrderCreated>;	
};	
const broker = new RabbitMQBroker("orders.consumer");	
const sub =	await broker	
 .queue("orders.q")	
 .exchange<Contracts>("	orders.ex", {
exchangeType:	"topic",
routingKey:	"order.*", // <-- binding pattern 	});	sub.handle("	order.created", async (_id,	ev) => {console.log(ev.data.orderId);});await sub.consume({prefetch:	10,	concurrency:	5});

Practical Applications

    • E‑commerce system – Queue binds to ‘payment.#’ to receive all payment-related events across multiple services; publisher sends concrete keys like ‘payment.processed.v2’ or ‘payment.refund.initiated’.
    • Audit logging – Queue binds to ’#’ capturing every message for archival; publishers must still use meaningful non‑wildcard keys so log analysis tools can filter by origin.
    • Pitfall – Publishing any message with a literal ’.’ inside a wordless segment breaks multi‑word matching unexpectedly; always keep published keys simple dot‑separated nouns without extra characters.

References:

Continue reading

Next article

How to Fix AI Coding Agents' Blind Spots with a 5-Minute Named-Persona Review

Related Content