Broker setup recipes
A copy-paste recipe per SDK for the two most common brokers — Redis and
RabbitMQ — so a message produced in one language is consumed in another over the
broker you already run. Every recipe emits and reads the exact
canonical envelope (schema_version: 1); only the wiring
differs. The body is identical everywhere, so any pairing interoperates — see the
broker bindings for how each broker carries it
natively.
Seven brokers are GA — these recipes cover two of them. Redis and RabbitMQ are the worked examples below; the same shape extends to Amazon SQS, Azure Service Bus, Apache Pulsar, Apache Kafka and Apache ActiveMQ/Artemis, all GA and conformance-locked. For those five, swap in the matching transport/extra — Go
…/sqs · …/pulsar · …/kafka · …/artemis · …/azureservicebus, Python[sqs] [pulsar] [kafka] [artemis](+ Azure SB), Node@babelqueue/sqs · …/pulsar · …/kafka · …/artemis · …/azure-service-bus, Javababelqueue-sqs/-pulsar/-kafka/-artemis/-azureservicebus, .NETBabelQueue.Sqs/.Pulsar/.Kafka/.Artemis/.AzureServiceBus, and PHPphp-sdkSqsTransport/KafkaTransport+KafkaConsumer/PulsarTransport+PulsarConsumer/StompTransport(Artemis) plus the Laravelbabelqueue-sqsandbabelqueue-artemisdrivers. The one documented gap is PHP × Azure Service Bus. The per-broker projection is in the broker bindings; the keys are in the configuration reference.
Runtime vs core-only. The shapes differ by language: a runtime SDK (Laravel, Symfony, Python, Go’s
App, Node adapters) gives youpublish/worker out of the box, while a core-only SDK (.NET, Java core) is a codec — you move the bytes with your own broker client, or a dedicated transport where one ships (e.g.BabelQueue.Redis,com.babelqueue:babelqueue-redis). Where a language has no first-party RabbitMQ transport on the core, the recipe uses its framework adapter (MassTransit, Spring AMQP), which is the supported path.
All recipes use the same queue (orders) and URN (urn:babel:orders:created) so you
can mix any producer with any consumer.
PHP / Laravel
Laravel plugs in as a custom queue connection. Pick the driver per broker in
config/queue.php:
// config/queue.php — Redis
'connections' => [
'babelqueue' => [
'driver' => 'babelqueue-redis',
'connection' => 'default',
'queue' => env('BABELQUEUE_QUEUE', 'orders'),
'retry_after' => 90,
],
],
// config/queue.php — RabbitMQ
'connections' => [
'babelqueue' => [
'driver' => 'babelqueue-rabbitmq',
'host' => env('RABBITMQ_HOST', '127.0.0.1'),
'port' => env('RABBITMQ_PORT', 5672),
'queue' => env('BABELQUEUE_QUEUE', 'orders'),
],
],
Publish with the facade and run the standard worker against the connection:
use BabelQueue\Facades\BabelQueue;
BabelQueue::publish('urn:babel:orders:created', ['order_id' => 1042]);
php artisan queue:work babelqueue
URN→handler mapping and the dead-letter policy live in config/babelqueue.php — see
Laravel configuration.
PHP / Symfony
Symfony Messenger keeps its own transport DSN — redis:// or amqp:// — and you swap
in the BabelQueue serializer so the wire format is the canonical envelope:
# config/packages/messenger.yaml
framework:
messenger:
transports:
babel:
dsn: '%env(MESSENGER_TRANSPORT_DSN)%' # redis://localhost:6379/messages
# or amqp://guest:guest@localhost:5672/%2f/messages
serializer: 'babelqueue.messenger.serializer'
routing:
'App\Message\OrderCreated': babel
Map inbound URNs back to message classes in config/packages/babelqueue.yaml, then
dispatch and consume the Messenger way (messenger:consume babel). See
Symfony configuration.
PHP (framework-less core)
The babelqueue/php-sdk core is a codec; its optional reference transports move the
bytes. RedisTransport (over predis/predis) and AmqpTransport (over
php-amqplib) both publish the encoded envelope:
use BabelQueue\Codec\EnvelopeCodec;
use BabelQueue\Transport\RedisTransport;
$transport = new RedisTransport(new Predis\Client('redis://localhost:6379'));
$env = EnvelopeCodec::make('urn:babel:orders:created', ['order_id' => 1042], 'orders');
$transport->publish(EnvelopeCodec::encode($env), 'orders');
Swap RedisTransport for AmqpTransport to publish to RabbitMQ — see
PHP transports.
Python
The Python runtime chooses its transport from the broker URL scheme — redis:// or
amqp://. Install the matching extra:
# pip install "babelqueue[redis]" → Redis
# pip install "babelqueue[amqp]" → RabbitMQ (via pika)
from babelqueue import BabelQueue
app = BabelQueue("redis://localhost:6379/0", queue="orders")
# app = BabelQueue("amqp://guest:guest@localhost:5672/", queue="orders")
app.publish("urn:babel:orders:created", {"order_id": 1042})
Register handlers and run the worker as in Python configuration.
Go
The Go core is a codec; the optional zero-dependency App runtime plus a per-broker
transport module gives you publish/consume. Install the module you need:
// go get github.com/babelqueue/babelqueue-go/redis → Redis
// go get github.com/babelqueue/babelqueue-go/amqp → RabbitMQ
import (
babelqueue "github.com/babelqueue/babelqueue-go"
"github.com/babelqueue/babelqueue-go/redis"
)
tr, err := redis.New("redis://localhost:6379/0") // amqp.New("amqp://guest:guest@localhost:5672/")
if err != nil {
return err
}
defer tr.Close()
app := babelqueue.NewApp(tr, babelqueue.WithDefaultQueue("orders"))
app.Handle("urn:babel:orders:created", func(ctx context.Context, env babelqueue.Envelope) error {
return nil // env.Data, env.TraceID
})
app.Publish(ctx, "urn:babel:orders:created", map[string]any{"order_id": 1042})
return app.Consume(ctx) // blocks
The Redis transport runs the reliable-queue pattern; AMQP uses durable queues with manual ack. See Runtime & transports.
Node.js
Beyond @babelqueue/core, thin transport packages wire it to each broker — you bring
the client:
// npm install @babelqueue/redis ioredis → Redis
import Redis from "ioredis";
import { RedisPublisher, RedisConsumer } from "@babelqueue/redis";
const client = new Redis("redis://localhost:6379/0");
await RedisPublisher.create(client, "orders")
.publish("urn:babel:orders:created", { order_id: 1042 });
const consumer = new RedisConsumer(client, "orders", {
"urn:babel:orders:created": (env) => console.log(env.data.order_id, env.trace_id),
}, { maxTries: 3 });
await consumer.run(() => true);
// npm install @babelqueue/rabbitmq amqplib → RabbitMQ
import amqp from "amqplib";
import { RabbitMQPublisher } from "@babelqueue/rabbitmq";
const channel = await (await amqp.connect("amqp://guest:guest@localhost:5672/")).createChannel();
await channel.assertQueue("orders", { durable: true });
await RabbitMQPublisher.create(channel, "orders")
.publish("urn:babel:orders:created", { order_id: 1042 });
For a BullMQ/NestJS stack on Redis, use @babelqueue/bullmq /
@babelqueue/nestjs — see Adapters & transports.
Java
The Java core is a codec. For Redis it ships a first-party transport,
com.babelqueue:babelqueue-redis (built on Lettuce):
import com.babelqueue.redis.RedisPublisher;
import io.lettuce.core.RedisClient;
import io.lettuce.core.api.sync.RedisCommands;
import java.util.Map;
RedisClient client = RedisClient.create("redis://localhost:6379");
RedisCommands<String, String> redis = client.connect().sync();
RedisPublisher.create(redis, "orders")
.publish("urn:babel:orders:created", Map.of("order_id", 1042L));
For RabbitMQ, use the Spring Boot adapter com.babelqueue:babelqueue-spring: its
auto-configured MessageConverter wires the canonical envelope into RabbitTemplate
(producing) and @RabbitListener (consuming):
@Service
class Orders {
private final BabelQueuePublisher babelQueue;
Orders(BabelQueuePublisher babelQueue) { this.babelQueue = babelQueue; }
void create() {
babelQueue.publish("urn:babel:orders:created", Map.of("order_id", 1042L), "orders");
}
}
See the Redis transport and Spring Boot adapter.
.NET
The .NET core is a codec. For Redis it ships BabelQueue.Redis (built on
StackExchange.Redis):
using BabelQueue.Redis;
using StackExchange.Redis;
var redis = await ConnectionMultiplexer.ConnectAsync("localhost:6379");
var db = redis.GetDatabase();
await new RedisPublisher(db, "orders")
.PublishAsync("urn:babel:orders:created", new Dictionary<string, object?> { ["order_id"] = 1042 });
For RabbitMQ, use the MassTransit adapter BabelQueue.MassTransit: register its
System.Text.Json envelope converter so MassTransit’s RabbitMQ transport carries the
canonical envelope. (You can also encode with EnvelopeCodec.Encode(...) and publish
with your own RabbitMQ.Client channel.) See the
Redis transport and
MassTransit adapter.
Cross-language sanity check
Because every recipe writes the same envelope to the same orders queue, you can
mix producers and consumers freely — e.g. a Python redis:// producer and a Go
…/redis consumer, or a Laravel babelqueue-rabbitmq producer and a Node
@babelqueue/rabbitmq consumer. For a runnable end-to-end demo, see
Cross-language: Python → Go over Redis.
Continue to Dead-letter & tracing.