An ergonomic RabbitMQ / AMQP messaging library for Gleam, built on the BEAM and OTP.
Small, quick, and busy — a wren is a tiny bird that does a great deal. This library aims to do the same for your messages: a friendly, type-safe layer over AMQP that lets OTP handle the hard parts (supervision, reconnection, fault tolerance) so you don't have to.
Stable — 1.0. The public API is settled and the whole feature set is
exercised by an integration suite against a real broker (green in CI, which
stands up its own RabbitMQ):
- ✅ Typed connections & channels over the Erlang
amqp_client - ✅ A supervised, actor-based consumer (OTP restarts and re-subscribes on crash)
- ✅ Producer options — exchange, routing key, headers, priority, expiration,
mandatory - ✅ A
Codecabstraction with a JSON codec, plus typedpublish_encoded/decode_message - ✅ Router-style consumer — dispatch by message
kindto typed handlers, with a fallback - ✅ Topology management — exchanges, bindings, deletes, and
x-*queue/exchange arguments - ✅ Retry policy & metadata — backoff strategies, header round-tripping (
wren/retry) - ✅ Retry/dead-letter infrastructure — delay queues with TTL, dead-letter exchange, DLQ
- ✅ Connection recovery — self-healing consumer that reconnects with backoff, plus QoS prefetch
- ✅ Client config & ergonomics —
config_from_env, vhost/heartbeat/timeout, aClientfront door
And, since v0.2:
- ✅ Publisher confirms (
publish_confirmed) + persistent delivery (with_persistence) - ✅ Concurrent delivery processing, bounded by prefetch (
start_consumer_concurrent) - ✅ Connection pool (
start_pool) with round-robin channel handout andpool_stats - ✅ Active
health_check, kind-based producer (publish_for_kind), TLS, and topology guards - ✅ Consumer subscribe options — auto-ack, exclusive, no-local, consumer tag, subscription arguments
- ✅ Full AMQP message properties (
correlation_id/reply_tofor RPC, …) and batch / multi-target publishing - ✅ Raw byte (
BitArray) payloads, withpublish_text/message_textfor the text case
See ROADMAP.md for how each piece maps to a production AMQP
client, and CHANGELOG.md for what landed when.
- A typed, router-style API for consuming messages by kind.
- Producers with sensible defaults and explicit routing.
- Retry and dead-letter handling driven by message headers and broker topology.
- Connection recovery that leans on OTP supervision rather than hand-rolled loops.
Publish a typed message with a kind, then consume and decode it:
import wren
import wren/codec
// A codec is just an encoder + decoder; `codec.json` builds one from gleam_json.
let order_codec = codec.json(encode_order, order_decoder)
// Publish, tagging the message with its kind for routing.
let options =
wren.publish_options()
|> wren.route("orders")
|> wren.with_kind("order.created")
let assert Ok(_) = wren.publish_encoded(channel, order, order_codec, options)
// Consume under supervision, routing by kind to typed handlers.
let router =
wren.router()
|> wren.handle("order.created", order_codec, fn(order: Order) {
handle(order)
wren.Ack
})
|> wren.fallback(fn(_message) { wren.Reject })
let assert Ok(_) = wren.start_router(channel, "orders", router)Handlers receive the already-decoded value; a malformed payload is rejected and
logged before it ever reaches your code. Need the headers or routing key too?
Reach for handle_with, which also hands you the raw Message.
Give the consumer a retry policy and wren builds the delay-queue + dead-letter
topology for you. A handler returning wren.Retry redelivers the message after
a backoff (via a TTL'd delay queue); once attempts are exhausted — or on
wren.DeadLetter — it goes to the dead-letter queue.
import wren/retry
let infra =
wren.retry_infrastructure(
"orders",
retry.RetryPolicy(
strategy: retry.ExponentialBackoff(
initial_ms: 1000,
max_ms: 60_000,
multiplier: 2.0,
),
max_attempts: 5,
),
)
// Declares the topology and starts consuming, routing by kind.
let assert Ok(_) = wren.start_router_with_retry(channel, router, infra)Runnable, self-contained programs live under src/wren/examples:
gleam run -m wren/examples/router # dispatch typed messages by kind
gleam run -m wren/examples/retry # fail once, then succeed via a delay queue
gleam run -m wren/examples/recovery # a self-healing consumer
gleam run -m wren/demo # the original supervised-consumer demoEverything talks to a real broker. Bring one up first:
docker compose up -d # start a local RabbitMQ (wren / wren)
gleam test # run the integration + unit suites
gleam run -m wren/examples/router
docker compose down # stop the brokerReleased under the MIT licence.