Cookbook
Practical recipes for common scheduling jobs. Each one is self-contained: copy it, adjust the expression and the work, and run.
New to the API? Start with the Quickstart and Cron Syntax.
Daily backup at 3 AM (background)
Heavy jobs belong in a background task so they don't block your event loop. Run it in a fixed timezone so "3 AM" is unambiguous.
// app.js
import cron from 'node-cron';
const backup = cron.schedule('0 3 * * *', './tasks/backup.js', {
name: 'daily-backup',
timezone: 'America/Sao_Paulo',
noOverlap: true,
});
backup.on('execution:failed', (ctx) => {
console.error('backup failed:', ctx.execution?.error?.message);
});// tasks/backup.js
export async function task() {
await dumpDatabase();
await uploadToStorage();
}Periodic cleanup every 15 minutes
import cron from 'node-cron';
cron.schedule('*/15 * * * *', async () => {
const removed = await deleteExpiredSessions();
console.log(`cleaned up ${removed} expired sessions`);
});Timezone-aware health check on weekdays
Run only during business hours (09:00–17:00, Mon–Fri) in a specific timezone.
import cron from 'node-cron';
cron.schedule('0 9-17 * * 1-5', async () => {
const ok = await pingService();
if (!ok) await notifyOnCall();
}, {
name: 'business-hours-healthcheck',
timezone: 'Europe/London',
});Spread load with jitter
When many instances run the same hourly job, stagger them with up to 30s of random delay to avoid a thundering herd.
import cron from 'node-cron';
cron.schedule('0 * * * *', async () => {
await refreshSharedCache();
}, {
maxRandomDelay: 30_000,
});Retry on failure
React to execution:failed and re-run the task with a backoff. execute() triggers an immediate, off-schedule run.
import cron from 'node-cron';
const task = cron.schedule('*/10 * * * *', async () => {
await syncRemoteData(); // throws on failure
});
const MAX_RETRIES = 3;
let attempts = 0;
task.on('execution:failed', async (ctx) => {
if (attempts >= MAX_RETRIES) {
attempts = 0;
console.error('giving up after retries:', ctx.execution?.error?.message);
return;
}
attempts++;
const delay = 1000 * 2 ** attempts; // exponential backoff
console.warn(`retry ${attempts} in ${delay}ms`);
setTimeout(() => task.execute().catch(() => {}), delay);
});
task.on('execution:finished', () => {
attempts = 0; // reset after a clean run
});Run exactly once
Set maxExecutions: 1 and the task destroys itself after a single run. Combine with execute() for an immediate one-shot.
import cron from 'node-cron';
// Runs once at the next minute boundary, then self-destructs.
cron.schedule('* * * * *', () => {
console.log('one-time job');
}, {
maxExecutions: 1,
});Inspect everything that's scheduled
getTasks() returns every registered task, handy for a status endpoint or dashboard.
import cron from 'node-cron';
function listTasks() {
return [...cron.getTasks().values()].map((task) => ({
id: task.id,
name: task.name,
status: task.getStatus(),
nextRun: task.getNextRun(),
}));
}
console.table(listTasks());Graceful shutdown
Stop and destroy every task when the process is terminating. destroy() is async for background tasks, so await them all.
import cron from 'node-cron';
async function shutdown() {
await Promise.all(
[...cron.getTasks().values()].map((task) => task.destroy())
);
process.exit(0);
}
process.on('SIGTERM', shutdown);
process.on('SIGINT', shutdown);Next steps
- API Reference: every function in the module.
- Events & Observability: the events these recipes hook into.