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/cronDeclare
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
- defineWorkflow — for multi-step processes with timers and correlation
- Multi-transport recipe — running cron alongside HTTP and queue