The JavaScript client is where the builder shape originated. Every SDK implements the same surface natively in its own idiom, and the Rust client shares its wire types with the broker. This one is ESM only, Node 22 or newer, with the broker client and the streaming SDK in one package.
npm i queen-mqyarn add queen-mqpnpm add queen-mqbun add queen-mqimport { Queen } from 'queen-mq'
const client = new Queen({
urls: ['http://broker-a:6632', 'http://broker-b:6632'],
bearerToken: process.env.QUEEN_TOKEN,
loadBalancingStrategy: 'affinity',
})The constructor also takes a bare URL string or an array of them. One URL is a direct client;
more than one builds a load balancer that fails over on 5xx and network errors. The whole
option table is in Reference.
Push
const res = await client
.queue('orders')
.partition('customer-42')
.push([{ data: { orderId: 9137, amount: 99.5 } }])The await returns the broker’s array, one entry per item, each carrying a status of queued,
duplicate, buffered or failed. Pass your own transactionId and the push becomes
idempotent inside the dedup window.
Consume
await client
.queue('orders')
.group('billing')
.subscriptionMode('all')
.limit(1)
.each()
.consume(async (message) => {
console.log(message.data)
})consume() starts concurrency workers and resolves only when every worker stops, so give it a
limit, an idleMillis or an AbortSignal if it is ever meant to return. autoAck is on by
default: the worker acks completed when the handler returns, failed when it throws.
The builder is where a consumer is shaped: .group() for the consumer group, .partitions()
for how many lanes one pop claims, .batch() for how many messages come back, .renewLease()
for a handler that runs long.
Acknowledge
const messages = await client.queue('orders').group('billing').batch(10).pop()
for (const msg of messages) { await handle(msg) }
await client.ack(messages, true, { group: 'billing' })ack() takes one message or an array, and a status that is true/false or one of
completed, failed, retry, dlq. A rejected ack still arrives as HTTP 200, so read
success off each item. To ack the input and push the output in one PostgreSQL commit, use
client.transaction().
What differs here
- Without
.each()the handler receives the array of popped messages, even whenbatchis1. .onSuccess()or.onError()turnautoAckoff: if your callback never acks, the lease expires.pop()swallows every failure into[], so an empty array is not an empty queue: useconsume()when failures must surface.close()is not optional, because theundicikeep-alive sockets pin the event loop and the process never exits.headers: { Host }is dropped silently byfetch, so the client maps it ontohostHeaderand warns.
Buffering, lease renewal, admin, the dead-letter reader, logging and every option table are in the JavaScript client reference.