How to Publish Messages with Routing Keys in Kourier: A Complete Guide
Kourier exposes routing key functionality through the AMQPChannel.basicPublish() method, where the routingKey parameter determines which bound queues receive the message based on the exchange type and binding configuration.
The Kourier library (guimauvedigital/kourier) provides a Kotlin multiplatform AMQP 0-9-1 client that implements standard message routing semantics. When publishing messages, the routing key acts as the address label that exchanges use to determine message delivery paths to specific queues.
The basicPublish API and Routing Key Semantics
Kourier follows the AMQP 0-9-1 model where a publisher sends a message to an exchange together with a routing key. The exchange uses that key (and the exchange type) to decide which queues receive the message.
The publishing API is exposed via the AMQPChannel interface in amqp-client/src/commonMain/kotlin/dev/kourier/amqp/channel/AMQPChannel.kt:
suspend fun basicPublish(
body: ByteArray,
exchange: String,
routingKey: String,
mandatory: Boolean = false,
immediate: Boolean = false,
properties: Properties = Properties()
): AMQPResponse.Channel.Basic.Published
exchange– Name of the target exchange (empty string uses the default direct exchange).routingKey– The string that the exchange matches against its bindings.mandatory/immediate– Optional flags that affect undeliverable messages.
The concrete implementation in DefaultAMQPChannel.kt encodes the routing key into the Basic.Publish frame before sending the payload to the broker (lines 31-45).
Publishing to a Direct Exchange
To publish messages with routing keys in Kourier, declare a direct exchange and invoke basicPublish with the desired key. The following example from the official tutorial demonstrates publishing log messages with severity levels as routing keys:
val config = amqpConfig { server { host = "localhost" } }
val connection = createAMQPConnection(this, config)
val channel = connection.openChannel()
// Declare a direct exchange named "direct_logs"
channel.exchangeDeclare(
"direct_logs",
BuiltinExchangeType.DIRECT,
durable = false,
autoDelete = false,
internal = false,
arguments = emptyMap()
)
// Publish a message with severity "error" as the routing key
channel.basicPublish(
body = "Disk space critically low".toByteArray(),
exchange = "direct_logs",
routingKey = "error", // ← routing key
properties = Properties()
)
channel.close()
connection.close()
Implementation reference: This pattern appears in the emitLogDirect function demonstrated in docs/tutorials/routing.md (lines 96-124).
Binding Queues to Routing Keys
Publishing with a routing key only works if queues are bound to the exchange with matching binding keys. For a direct exchange, the message is delivered only to queues whose binding key exactly matches the routing key.
val queue = channel.queueDeclare(
name = "", // temporary, server‑generated name
durable = false,
exclusive = true,
autoDelete = true,
arguments = emptyMap()
).queueName
// Bind the temporary queue to the "direct_logs" exchange for "error" and "warning"
listOf("error", "warning").forEach { severity ->
channel.queueBind(
queue = queue,
exchange = "direct_logs",
routingKey = severity // ← binding key (must match routing key)
)
}
Implementation reference: The binding loop appears in docs/tutorials/routing.md (lines 68-74). The routing semantics for direct exchanges are documented in the same file (lines 46-48).
Consuming Messages and Accessing Routing Keys
When a message is received, the broker constructs an AMQPMessage that exposes the original routing key via the routingKey property. Consumers can inspect this value to determine how the message was routed.
val consumer = channel.basicConsume(queue, noAck = true)
for (delivery in consumer) {
val rk = delivery.message.routingKey // ↔ the routing key used when publishing
val payload = delivery.message.body.decodeToString()
println("Received [$rk]: $payload")
}
Implementation reference: The AMQPMessage data class in AMQPMessage.kt (lines 4-6) defines the routingKey field. The consumer loop pattern appears in docs/tutorials/routing.md (lines 81-88).
Exchange Types and Routing Behavior
The routing key only matters if the exchange type uses it:
- Direct exchange – Delivers messages to queues where the binding key exactly matches the routing key.
- Topic exchange – Uses the routing key for pattern matching against binding keys (wildcards supported).
- Fanout exchange – Ignores the routing key entirely and broadcasts to all bound queues.
Summary
- Use
AMQPChannel.basicPublish()with theroutingKeyparameter to publish messages with routing keys in Kourier. - Declare exchanges using
BuiltinExchangeType.DIRECTorTOPICto enable routing key matching. - Bind queues using
queueBind()with a binding key that matches your publishing routing keys. - Access routing keys during consumption via
delivery.message.routingKeyin theAMQPMessageobject. - Reference
RoutingTest.kt(lines 39-84) for an end-to-end integration test verifying routing behavior.
Frequently Asked Questions
What happens if I publish a message with an empty routing key?
When publishing to the default direct exchange (empty string exchange name), an empty routing key matches queues bound with an empty binding key. For named exchanges, an empty routing key only delivers to queues explicitly bound with an empty string binding key, which is uncommon in production systems.
How does Kourier handle routing keys for topic exchanges?
Kourier supports standard AMQP topic exchange semantics where routing keys are dot-separated strings (e.g., stock.nyse.tech). Consumers can use wildcard binding keys such as stock.* or stock.# to receive subsets of messages. The routingKey parameter in basicPublish() accepts any valid topic routing key string.
Can consumers retrieve the routing key from incoming messages?
Yes. The AMQPMessage class in AMQPMessage.kt exposes the routingKey property, allowing consumers to access the exact routing key used during publishing via delivery.message.routingKey. This enables content-based routing decisions or logging within the consumer application.
Where is the routing key encoding implemented in the Kourier source code?
The routing key is encoded into the AMQP Basic.Publish frame in DefaultAMQPChannel.kt (lines 31-45). This implementation takes the routingKey string from the basicPublish() call and writes it into the protocol frame sent to the broker, ensuring compatibility with standard AMQP 0-9-1 brokers.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →