Recurring jobs
A recurring job runs on a cron schedule rather than on demand. Define it with
defineRecurringJob. The worker announces the schedule when it registers, and
the server triggers the job on the cadence you specify.
import { defineRecurringJob } from "@zeridion/flare/worker";
const nightlyCleanup = defineRecurringJob({
jobType: "NightlyCleanup",
cron: "0 3 * * *",
timezone: "UTC",
queue: "maintenance",
async handle(ctx) {
await db.sessions.deleteExpired({ signal: ctx.signal });
},
});
Options
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
jobType | string | yes | — | The routing key. Must be unique within the worker. |
cron | string | yes | — | A standard five-field cron expression (minute, hour, day-of-month, month, day-of-week). |
timezone | string | no | server default | The IANA timezone the cron is interpreted in (for example "UTC", "America/New_York"). |
queue | string | no | "default" | The queue the triggered job runs on. |
maxAttempts | number | no | 3 | Attempts per triggered run. |
timeoutSeconds | number | no | 1800 | Per-run timeout. |
Always set a timezone for non-UTC schedules
The timezone field is part of the schedule. If you omit it, the cron is
interpreted in the server's default timezone — which means a "0 9 * * *"
("9 AM daily") schedule will not fire at 9 AM in your local time unless that
default happens to match. Set timezone explicitly whenever the cadence is
time-of-day sensitive:
const morningDigest = defineRecurringJob({
jobType: "MorningDigest",
cron: "0 9 * * *",
timezone: "America/New_York", // 9 AM Eastern, not UTC
async handle(ctx) {
await sendDigest({ signal: ctx.signal });
},
});
The handler
A recurring handler takes only the job context — there is
no payload. The same contract as a payload job applies: return to acknowledge
success, throw to acknowledge failure, honour ctx.signal, and stay
idempotent (a run can be delivered more than once).
const rebuildSearchIndex = defineRecurringJob({
jobType: "RebuildSearchIndex",
cron: "*/30 * * * *", // every 30 minutes
timezone: "UTC",
timeoutSeconds: 600,
async handle(ctx) {
for (const batch of batches()) {
if (ctx.signal.aborted) break; // cooperative cancellation
await indexBatch(batch, { signal: ctx.signal });
ctx.reportProgress(progressSoFar());
}
},
});
Registering the schedule
You don't call any extra method — listing the recurring definition in the
worker's jobs array is enough. The worker includes it in the schedules it
announces on startup:
const worker = new FlareWorker({
apiKey: process.env.FLARE_API_KEY,
jobs: [nightlyCleanup, morningDigest],
});
await worker.run();
See also
- Defining jobs — payload-bearing jobs.
- Job context — what a recurring handler receives.