Configuration
Broker defaults, exchange options, and topology ownership
Rabbit Relay keeps configuration explicit and close to RabbitMQ concepts. You configure defaults on the broker, then override them per queue/exchange interface when needed.
RabbitMQ connection URL
Rabbit Relay reads the connection URL from the RABBITMQ_URL environment
variable:
RABBITMQ_URL=amqp://user:password@localhost:5672Or override it per broker:
const broker = new RabbitMQBroker("orders-service", {
connectionUrl: "amqp://user:password@rabbitmq.internal:5672",
connectionName: "orders-service-primary",
shutdownTimeoutMs: 30_000,
});Prop
Type
Each broker owns its own connection; closing one broker never closes another broker's resources.
Common topology options
Every .exchange() call accepts these overrides on top of broker defaults:
Prop
Type
Topology ownership mode
Decide who owns RabbitMQ topology:
// App declares everything (default)
const broker = new RabbitMQBroker("orders-service", {
topologyMode: "assert",
});Infrastructure-managed deployments
Prefer topologyMode: "passive" over the legacy passiveQueue flag.
passiveQueue remains supported but only controls main-queue declaration.
Publisher confirms
Disabled by default. Enable when the publisher must know that RabbitMQ accepted the message:
const pub = await broker.exchange("orders.ex", { publisherConfirms: true });Message size guard
const broker = new RabbitMQBroker("orders-service", {
maxMessageBytes: 256 * 1024,
});
// override per publish
await sub.publish(envelope, { maxMessageBytes: 64 * 1024 });App-owned topology
Use the default mode when the application owns topology.
const broker = new RabbitMQBroker("orders-service", {
topologyMode: "assert",
});Rabbit Relay declares:
- exchange
- queue
- binding
- configured DLQ topology
- delayed retry topology when used
Infrastructure-owned topology
Use passive mode when topology is created before the app starts.
const broker = new RabbitMQBroker("orders-service", {
topologyMode: "passive",
});Rabbit Relay checks that required exchanges and queues exist. It does not declare or bind topology. If required topology is missing, startup fails early.
CI / review topology mode
Use plan-only mode when you want Rabbit Relay to build a topology plan without RabbitMQ topology setup calls.
const broker = new RabbitMQBroker("orders-service", {
topologyMode: "plan-only",
});
const sub = await broker
.queue("orders.q")
.exchange("orders.ex", {
exchangeType: "topic",
routingKey: "orders.*",
});
console.log(broker.planTopology());This is useful for:
- CI checks
- DevOps review
- docs generation
- comparing code topology with infrastructure topology
Dead-letter configuration
const sub = await broker
.queue("orders.q")
.exchange("orders.ex", {
deadLetter: {
exchange: "orders.dlx",
queue: "orders.dlq",
routingKey: "orders.dead",
autoDeclare: true,
},
});When autoDeclare: true, Rabbit Relay includes DLQ topology in assertion
and planning behavior.
deadLetter.routingKey sets x-dead-letter-routing-key on the source queue
and, when autoDeclare is on, is also used as the DLQ→DLX binding key.
Omitting it preserves original routing keys and binds the DLQ to "#".
With topologyMode: "passive", Rabbit Relay checks the configured DLX/DLQ
exist instead of declaring them. With topologyMode: "plan-only", Rabbit
Relay records them in the topology plan without setup calls.
Native amqplib options
Use amqp when you need RabbitMQ-specific options not directly modeled by
Rabbit Relay.
const sub = await broker
.queue("orders.q")
.exchange("orders.ex", {
amqp: {
queue: {
arguments: {
"x-queue-type": "quorum",
},
},
publish: {
persistent: true,
},
},
});Using config from plain JavaScript or JSON
A common setup keeps topology config outside TypeScript (a shared .mjs
module or JSON file). TypeScript widens literal values there, so they stop
matching relay's option unions:
// platform.mjs
export const EXCHANGES = [{ name: "orders.events", type: "topic" }];
// inferred inside TS as: { name: string, type: string }[]Passing EXCHANGES[0].type to exchangeType then fails typecheck, because
the option expects "topic" | "direct" | "fanout" | "headers" and receives
string.
Rabbit Relay exports reusable aliases so you never have to hand-copy union
members out of .d.ts files. Annotate the config at its source using a
JSDoc import type:
// platform.mjs
/** @typedef {import("@bitspacerlabs/rabbit-relay").ExchangeType} ExchangeType */
/** @type {{ name: string, type: ExchangeType }[]} */
export const EXCHANGES = [{ name: "orders.events", type: "topic" }];In TypeScript files, import the alias directly:
import type { ExchangeType } from "@bitspacerlabs/rabbit-relay";
const type: ExchangeType = EXCHANGES[0].type;
await broker.queue("orders.q").exchange("orders.events", {
exchangeType: type,
});Available aliases:
| Alias | Values | Used by |
|---|---|---|
ExchangeType | "topic" | "direct" | "fanout" | "headers" | exchangeType, deadLetter.exchangeType |
TopologyMode | "assert" | "passive" | "plan-only" | topologyMode |
ErrorAction | "ack" | "requeue" | "dead-letter" | "retry" | onError |
RetryThenAction | "ack" | "requeue" | "dead-letter" | retry.then |
Recommended environment 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" |
Summary
- Configure defaults on
RabbitMQBroker - Override per
.exchange(...) - Use
topologyModeto make topology ownership explicit - Prefer
topologyMode: "passive"for infrastructure-managed RabbitMQ - Keep
passiveQueueonly for legacy queue-only passive behavior