Cron vs setInterval in Node.js — Which Should You Use?
When you need to run code on a schedule in Node.js you have two main options: JavaScript's built-in setInterval or a cron-based library like node-cron. Both work, but they solve different problems. Picking the wrong one leads to subtle bugs around drift, missed runs, and time zone handling.
setInterval — the quick option
setInterval runs a function every N milliseconds from the moment it is called. It does not know about wall-clock time.
// Run every 5 minutes
setInterval(() => {
console.log('tick:', new Date().toISOString());
doWork();
}, 5 * 60 * 1000);Pros: zero dependencies, simple, good for fixed intervals.
Cons: drifts over time (each callback adds a tiny delay), restarts reset the clock, can't express "every Monday at 9 AM", no timezone awareness.
node-cron — wall-clock scheduling
node-cron fires tasks at exact calendar times using cron expressions.
import cron from 'node-cron';
// Every weekday at 9:00 AM in Asia/Kolkata timezone
cron.schedule('0 9 * * 1-5', () => {
console.log('Morning job running:', new Date().toISOString());
doWork();
}, {
timezone: 'Asia/Kolkata'
});Pros: expressive schedules, timezone support, survives daylight saving changes, aligns to wall-clock minutes.
Cons: adds a dependency, slightly more setup.
The cron package (alternative)
The cron npm package offers a similar API with CronJob objects that can be started and stopped programmatically:
import { CronJob } from 'cron';
const job = new CronJob(
'0 0 * * 0', // every Sunday at midnight
() => { weeklyReport(); },
null, // onComplete
true, // start immediately
'Asia/Kolkata'
);
// Stop after first run
job.stop();Decision guide
- Use setInterval for simple, interval-based tasks where exact wall-clock time does not matter (e.g., polling an API every 30 seconds).
- Use node-cron or cron when the task must run at a specific time of day, day of week, or on a calendar-based schedule.
- Use an external scheduler (GitHub Actions, AWS EventBridge, Render cron jobs) when your Node.js server may restart or scale to multiple instances — you don't want duplicate job runs.
Handling overlapping runs
If your task takes longer than the schedule interval, you can end up with overlapping executions. Use a simple flag to prevent this:
let running = false;
cron.schedule('*/5 * * * *', async () => {
if (running) return; // skip if previous run hasn't finished
running = true;
try {
await doWork();
} finally {
running = false;
}
});Build your cron expression
Use the Dev Brains AI Cron Expression Generator to convert plain English like "every 5 minutes" or "every weekday at 9am" into the correct cron string for node-cron.