Treat the schedule as a trigger, not the worker
A scheduled HTTP request can start work, but it should not be responsible for every slow step. Hosting platforms impose request deadlines, deployments can interrupt execution, and two triggers can overlap. Use the scheduled endpoint to authenticate the caller, claim due work, enqueue bounded jobs, and return a useful result.
Scheduler
Decides when a workflow is due and sends a signed or platform-authenticated trigger.
Dispatcher
Claims a run, records its identity, and creates small independent work items.
Worker
Performs retryable work with deadlines, idempotency, and durable outcomes.
Very small tasks can complete inside the trigger request, but they still need duplicate protection and observable failure behavior.
Authenticate scheduled endpoints like control-plane APIs
A cron route can send email, modify records, generate reports, or call expensive providers. Never rely on an obscure URL. Verify a server-side secret or trusted platform signature before parsing work, use POST where state changes, and reject unexpected content types and payloads.
export default async function handler(req, res) {
if (req.headers.authorization !== "Bearer " + process.env.CRON_SECRET) {
return res.status(401).json({ ok: false });
}
const run = await claimScheduledRun("daily-report", new Date());
if (!run.claimed) return res.status(200).json({ ok: true, duplicate: true });
await enqueueReport({ runId: run.id });
return res.status(202).json({ ok: true, runId: run.id });
}Do not place secrets in query strings, logs, client bundles, or report links. Rotate them and keep staging schedules isolated from production data.
Assume every schedule can fire twice
Schedulers retry, deployments race, clocks drift, operators run jobs manually, and network timeouts hide successful responses. Assign every logical run a stable key such as job name plus intended time window, then enforce uniqueness in durable storage.
- Claim work with an atomic insert, compare-and-set, or database lock.
- Give every external side effect its own idempotency key.
- Separate “started,” “completed,” “failed,” and “unknown” outcomes.
- Expire run records only after the business retry window closes.
Split large runs into bounded batches
A monthly report for ten accounts is different from a nightly process for one million records. Read a limited page of work, enqueue independent items, and save a durable cursor. Keep concurrency below database, API, and email-provider limits.
Use a queue when work outlives one request, needs controlled retries, or must absorb bursts. The deeper worker, retry, and dead-letter patterns in our Node.js background-jobs guide apply directly behind a Next.js trigger.
Model time zones and missed runs explicitly
Store scheduler timestamps in UTC and convert to a business time zone only when the requirement truly follows a local calendar. Daylight-saving transitions can skip or repeat local times. Month-end, holidays, and billing cutoffs need product rules rather than date arithmetic hidden in a handler.
- Record both the intended run window and the actual start time.
- Decide whether a missed run should catch up, merge, or be skipped.
- Set a lateness limit so yesterday's action cannot execute unexpectedly.
- Use a testable clock abstraction for calendar and retry logic.
Make every run explainable and recoverable
Record job name, logical run ID, intended time, start and finish times, attempt, claimed items, successes, failures, duration, and deployment version. Keep payloads and personal data out of metric labels. Alert on missed schedules, sustained failures, growing lag, stuck runs, and unusual work volume.
Provide an audited replay path that targets a known window or failed item rather than blindly rerunning the entire schedule. Connect job evidence with request IDs and dependency traces using our Next.js observability guide.
Next.js scheduled jobs checklist
✓ Triggers authenticate before doing work
✓ Logical runs have durable unique keys
✓ Side effects are idempotent
✓ Large workloads use bounded batches
✓ Queue concurrency protects dependencies
✓ Time-zone and missed-run rules are explicit
✓ Every run has searchable operational evidence
✓ Manual replay is targeted and audited
Build a more dependable Next.js application
Endurance Softwares helps teams design, build, test, and operate production-ready Next.js platforms.
