---
title: "JS Client"
description: "Install queen-mq for Node, then push, consume and ack, plus the handful of traps that belong to JavaScript alone."
---

> Queen MQ documentation, for AI agents
> Complete self-contained summary of Queen MQ: https://queenmq.com/llms-brief.txt
> Fetch that first when the question is about the product rather than about this page.
> Index of all pages: https://queenmq.com/llms.txt

# JS Client

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.

```sh
npm install queen-mq
pnpm add queen-mq
yarn add queen-mq
bun add queen-mq
```

```js
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](/reference/sdk/javascript#constructor).

## Push

```js title="clients/client-js/test-v2/docs.js"
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

```js title="clients/client-js/test-v2/docs.js"
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

```js
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()`](/use/model#transactions).

## 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 `[]` except a conflation the broker cannot honour, 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](/reference/sdk/javascript).

Source: https://queenmq.com/use/js-client/index.mdx
