HalyardDocumentation

Installation

Install the package, run the migration, and start a worker.

Requirements

  • Postgres 13 or newer
  • Node.js 20 or newer

Postgres 13 is the floor because Halyard uses SKIP LOCKED, which is available earlier, and gen_random_uuid() from pgcrypto, which became built-in in 13.

Install

npm install halyard

Run the migration

Halyard ships its schema as a single idempotent migration. Run it once per database:

npx halyard migrate --database-url "$DATABASE_URL"

This creates one table (halyard_jobs) and two indexes. It does not create a schema, a role, or an extension — if your database policy requires those to be managed elsewhere, the SQL is printed with --dry-run so you can hand it to whoever owns migrations.

npx halyard migrate --dry-run

Define a job

import { defineJob } from "halyard";

export const sendReceipt = defineJob({
  name: "send-receipt",
  async handler({ orderId }: { orderId: string }) {
    const order = await orders.find(orderId);
    await mailer.send(order.email, receiptTemplate(order));
  },
});

The name is the durable identifier stored in the database. Changing it orphans any queued jobs using the old name, so treat it like a table name rather than a variable.

Start a worker

import { createWorker } from "halyard";
import { sendReceipt } from "./jobs/send-receipt";

const worker = createWorker({
  connectionString: process.env.DATABASE_URL,
  jobs: [sendReceipt],
  concurrency: 10,
});

await worker.start();

Run this as a separate process from your web server. It can be the same container image with a different command.

Verify

npx halyard status --database-url "$DATABASE_URL"
queued     0
running    0
failed     0
workers    1  (last heartbeat 2s ago)

If workers is 0, the worker process is not running or cannot reach the database.