Rabbit Relay

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";
ModeBehaviorBest for
"assert"Declare exchanges, queues, bindings, configured DLQ topology, and delayed retry topologylocal development, app-owned topology
"passive"Check required exchanges and queues exist without declaring themproduction with infra-managed topology
"plan-only"Build the topology plan but skip topology setup callsCI, 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:

  • assertExchange
  • assertQueue
  • bindQueue

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.


EnvironmentRecommended 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(...).

ModeplanTopology()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 startup
  • validateTopology() 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 topologyMode over passiveQueue for new infrastructure-managed setups

On this page