---
title: "Hello world"
description: "One message in, one message out, against a broker you started five minutes ago."
---

> 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

# Hello world

Nothing is created before it is used. The queue in the program below comes into existence with
the push that names it, inside the same transaction that stores the message, so there is no
declare step and nothing to provision.

The read takes the message under a **lease**: it is claimed until it is acknowledged or the lease
expires, and the acknowledgement is what commits consumption. No consumer group is named here, so
the read goes through the queue's own cursor, which starts at the beginning. Named groups are
[the next tutorial](/use/js-client/multi-queue-flow): a group created after a message was pushed
starts at the tail and would find nothing.

<pre class="mermaid">{`
sequenceDiagram
  participant C as client
  participant B as broker
  participant PG as PostgreSQL
  C->>B: push, naming a queue and a partition
  B->>PG: create both if absent, store the message
  PG-->>B: offset allocated
  B-->>C: 201, per-item status queued
  C->>B: pop, with a batch size and a wait
  B->>PG: claim the partition under a lease
  PG-->>B: messages from committed + 1
  B-->>C: 200, the messages and a leaseId
  C->>B: ack, by transactionId and partitionId
  B->>PG: move committed past it, release the lease
  B-->>C: 200, success true on the item
  Note over PG: nothing was deleted.<br/>The cursor moved.
`}</pre>

```js title="examples/tutorials/js/01-hello-world.mjs"
//
// Tutorial 1 of 5: hello world.
//
// One message in, one message out. Nothing is created in advance: the queue and
// the partition come into existence with the push that names them.
//
// Run it:
//   QUEEN_URL=http://localhost:6632 node 01-hello-world.mjs
//
// The program checks its own outcome and exits non-zero if a check fails.

import { Queen } from 'queen-mq'

const QUEEN_URL = process.env.QUEEN_URL || 'http://localhost:6632'

// The name is prefixed per language and suffixed per run, so every tutorial in
// every language can share one broker and no run inherits state from another.
const QUEUE = `tut-js-hello-${Date.now().toString(36)}`

let checks = 0
const assert = (condition, description) => {
  if (!condition) throw new Error(description)
  checks++
  console.log(`  ok: ${description}`)
}

// handleSignals: false leaves SIGINT and SIGTERM alone: this script owns its
// own shutdown, through close() at the bottom.
const queen = new Queen({ url: QUEEN_URL, handleSignals: false })

try {
  console.log(`broker ${QUEEN_URL}`)

  // A push names a queue and, optionally, a partition. Both are created by this
  // call if they do not exist, inside the transaction that stores the message.
  // There is no declare step and nothing to provision first.
  const [pushed] = await queen
    .queue(QUEUE)
    .push({ data: { greeting: 'Hello World!' } })

  console.log(`pushed ${pushed.transaction_id} -> ${pushed.status}`)
  assert(pushed.status === 'queued', 'the broker stored the message')

  // pop() takes messages under a lease: they are claimed until they are
  // acknowledged or the lease expires. wait(true) turns on long polling, so the
  // call parks until a message arrives instead of coming back empty.
  //
  // No consumer group is named here, so the read goes through the queue's own
  // cursor, which starts at the beginning. Named groups are tutorial 2: a group
  // created after a message was pushed starts at the tail and would see nothing
  // here.
  const messages = await queen
    .queue(QUEUE)
    .batch(1)
    .wait(true)
    .pop()

  assert(messages.length === 1, 'one message came back')
  const message = messages[0]
  console.log(`received "${message.data.greeting}" from partition ${message.partition}`)
  assert(message.data.greeting === 'Hello World!', 'the payload survived the round trip')

  // No partition was named on the push, so the broker put the message in the
  // queue's default lane.
  assert(message.partition === 'Default', 'it landed in the default partition')

  // The acknowledgement is what commits consumption. It moves the cursor past
  // the message and releases the lease. A rejected ack still arrives as HTTP
  // 200 with success: false on the item, so the per-item flag is the only proof
  // the broker took it.
  const ack = await queen.ack(message, true)
  assert(ack.success === true, 'the acknowledgement was accepted')

  // The cursor is now past the only message, so a further read finds nothing.
  // wait(false) returns immediately instead of long polling.
  const leftovers = await queen.queue(QUEUE).wait(false).pop()
  assert(leftovers.length === 0, 'the queue is drained')

  // Clean up on success only: a failed run leaves the queue on the broker to
  // be looked at.
  await queen.queue(QUEUE).delete()

  console.log(`\nPASS: ${checks} checks`)
} catch (err) {
  console.error(`\nFAIL: ${err.message}`)
  process.exitCode = 1
} finally {
  // close() flushes buffers and destroys the HTTP dispatcher. Without it the
  // keep-alive sockets hold the Node event loop open and the process hangs.
  await queen.close()
}
```

## Run it

Against a broker from [the quickstart](/start/quickstart), with the JavaScript client installed:

```bash
QUEEN_URL=http://localhost:6632 node 01-hello-world.mjs
```

The program checks its own outcome and exits non-zero if a check fails. Every tutorial on this
page runs in the repository's own suite: `examples/tutorials/run.sh js`.

Next: [Multi-queue flow](/use/js-client/multi-queue-flow).

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