Topology Modes
Control what Rabbit Relay does when a queue/exchange interface is created based on topology ownership.
Topology modes control what Rabbit Relay does when a queue/exchange interface is created.
They are useful when different environments have different ownership rules for RabbitMQ topology.
Modes
type TopologyMode = "assert" | "passive" | "plan-only";| Mode | Behavior | Best for |
|---|---|---|
"assert" | Declare exchanges, queues, bindings, configured DLQ topology, and delayed retry topology | local development, app-owned topology |
"passive" | Check required exchanges and queues exist without declaring them | production with infra-managed topology |
"plan-only" | Build the topology plan but skip topology setup calls | CI, docs, DevOps review |
Default mode: assert
assert is the default behavior.
const sub = await broker
.queue("orders.q")
.exchange("orders.ex", {
exchangeType: "topic",
routingKey: "orders.*",
});This mode calls RabbitMQ topology APIs such as:
assertExchangeassertQueuebindQueue
Use it when the application owns the topology.
Good for local development
assert is convenient when running RabbitMQ locally or in disposable test environments.
Passive mode
Use passive mode when infrastructure creates RabbitMQ topology before the application starts.
const sub = await broker
.queue("orders.q")
.exchange("orders.ex", {
exchangeType: "topic",
routingKey: "orders.*",
topologyMode: "passive",
});Rabbit Relay checks that required exchanges and queues exist.
If something is missing, startup fails early.
Good for production
Use topologyMode: "passive" when Terraform, Helm, RabbitMQ definitions, or DevOps scripts own RabbitMQ topology.
Plan-only mode
Use plan-only mode when you want Rabbit Relay to build its topology plan but not call RabbitMQ topology setup APIs.
const sub = await broker
.queue("orders.q")
.exchange("orders.ex", {
exchangeType: "topic",
routingKey: "orders.*",
topologyMode: "plan-only",
});
console.log(sub.planTopology());This is useful for:
- CI checks
- topology review
- documentation
- generating expected RabbitMQ definitions
- comparing app topology with infrastructure code
Plan-only does not publish or consume
plan-only skips topology setup calls. Normal publishing, consuming, validation, and redrive operations can still require RabbitMQ.
Where to set topology mode
You can set a default topology mode on the broker:
const broker = new RabbitMQBroker("orders-service", {
topologyMode: "passive",
});Or override it per exchange:
const sub = await broker
.queue("orders.q")
.exchange("orders.ex", {
topologyMode: "assert",
});Exchange-level settings override broker-level defaults.
Recommended setup
| Environment | Recommended mode |
|---|---|
| Local development | "assert" |
| Tests with disposable RabbitMQ | "assert" |
| CI topology review | "plan-only" |
| Production with app-owned topology | "assert" |
| Production with infra-owned topology | "passive" |
Relationship with topology planner
planTopology() is always read-only.
It returns what Rabbit Relay knows about the declared topology.
const plan = broker.planTopology();topologyMode controls what happens during .exchange(...).
| Mode | planTopology() | RabbitMQ topology changes |
|---|---|---|
"assert" | ✅ | ✅ |
"passive" | ✅ | ❌ |
"plan-only" | ✅ | ❌ |
See Topology Planner.
Relationship with topology validation
validateTopology() performs passive checks against RabbitMQ.
Passive mode uses the same idea during startup.
const result = await broker.validateTopology();Use:
topologyMode: "passive"to fail fast at startupvalidateTopology()when you want an explicit validation result object
See Topology Validation.
Delayed retry topology
If you use delayed retry, Rabbit Relay may need retry exchanges and retry queues.
With topologyMode: "assert", Rabbit Relay declares retry topology.
With topologyMode: "passive", Rabbit Relay checks that retry topology already exists.
With topologyMode: "plan-only", Rabbit Relay skips retry topology setup.
await sub.consume({
onError: "retry",
retry: {
attempts: 3,
delayMs: 5000,
then: "dead-letter",
},
});passiveQueue compatibility
passiveQueue is still supported for backward compatibility.
It only makes the main queue check passive.
For infrastructure-managed topology, prefer:
topologyMode: "passive"This is clearer because it applies to the topology behavior as a whole.
Queue arguments are immutable
RabbitMQ does not allow changing queue arguments after a queue already exists.
Examples of immutable queue arguments include:
- queue type
- dead-letter exchange
- dead-letter routing key
- message TTL
Local development reset
If you change queue arguments locally and get a precondition error, recreate the queue or reset the RabbitMQ volume.
Rabbit Relay reports these AMQP 406 PRECONDITION_FAILED errors with an actionable
message that names the offending argument (e.g. x-dead-letter-exchange) and the
resource, and suggests matching the arguments, using topologyMode: "passive", or
deleting the existing resource first.
Summary
- Use
"assert"when Rabbit Relay owns topology - Use
"passive"when infrastructure owns topology - Use
"plan-only"for CI/docs/review - Prefer
topologyModeoverpassiveQueuefor new infrastructure-managed setups