Skip to content

JS Client

Install queen-mq for Node, then push, consume and ack, plus the handful of traps that belong to JavaScript alone.

Updated View as Markdown

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-mq
import { 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

clients/client-js/test-v2/docs.jsjs
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

clients/client-js/test-v2/docs.jsjs
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 when batch is 1.
  • .onSuccess() or .onError() turn autoAck off: if your callback never acks, the lease expires.
  • pop() swallows every failure into [], so an empty array is not an empty queue: use consume() when failures must surface.
  • close() is not optional, because the undici keep-alive sockets pin the event loop and the process never exits.
  • headers: { Host } is dropped silently by fetch, so the client maps it onto hostHeader and warns.

Buffering, lease renewal, admin, the dead-letter reader, logging and every option table are in the JavaScript client reference.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close