Skip to content

Cron jobs

defineCron declares a schedule and the action it dispatches. The cron wire owns the timer and dispatches that action when the schedule fires.

Install

bash
pnpm add @nwire/cron

Declare

A cron points at an action — it doesn't run a handler inline. Put the work in the action:

ts
import { defineCron } from "@nwire/forge"

export const nightlyDigest = defineCron({
  name: "submissions.nightly-digest",
  schedule: "0 2 * * *",          // 02:00 every day
  dispatches: computeDigest,      // an action you defined
  buildInput: () => ({ now: new Date().toISOString() }),
})

Run it

createCronWire holds the schedule and dispatches into the apps you give it:

ts
import { createCronWire } from "@nwire/cron"

const cron = createCronWire({
  apps: [app],
  schedule: {
    "0 2 * * *": { action: "submissions.compute-digest" },
  },
})

await cron.start()

How the schedule fires

The wire holds an in-memory schedule and dispatches at the right moment. In split deployments, only the worker process starts the cron wire so the schedule fires once across the cluster.

Per-tenant schedules

A schedule entry can carry a tenant so the dispatch runs under that tenant's envelope. Omit it for a cluster-wide job (cache warm, metrics flush):

ts
const cron = createCronWire({
  apps: [app],
  schedule: {
    "0 2 * * *": { action: "submissions.compute-digest", tenant: "acme" },
    "*/5 * * * *": { action: "metrics.flush" },  // no tenant
  },
})

Testing

Cron just dispatches an action, so test the action directly and skip the wall clock:

ts
import { harness } from "@nwire/test-kit"

const h = await harness({ app })
await h.dispatch(computeDigest, { now: new Date().toISOString() })
expect(h.telemetry.count("event.published")).toBeGreaterThan(0)

See also

MIT licensed.