Introduction
Job scheduling refers to the process of triggering a job, task, or function at a predetermined time or when certain events occur. Most job schedulers are based on cron, the time-based job scheduler in Unix-like systems.
There are several job schedulers for the Node.js runtime environment. Some of these schedulers run both in the browser and Node, while others run only in Node. These schedulers each have their own unique features, so choosing the best one for your use case can be a daunting and time-consuming process.
In this article, I will take you through the most popular job schedulers for Node.js to highlight their key features, differences, and similarities.
In addition to the key features and functionalities offered by each package, I will highlight useful metrics and information such as bundle size, GitHub stars, release count, npm download statistics, package licenses, dependencies, and maintenances, among others.
A note on metrics
Some metrics, such as npm download counts, may be used as initial pointers towards packages to explore but shouldn’t be used to decide which package is better than another. According to npm, the download statistics are naive by design; the reported number of downloads far exceeds the number of actual package installations.
Similarly, GitHub stars are not any different from social media likes. They can be used as a proxy indicator for the popularity of a package but they don’t tell you much about the quality, stability, or maturity of the project.
On the other hand, metrics such as release count, bundle size, test coverage, dependencies, commit history, and age of the package, when looked at collectively, can give you some confidence about the package. You can then roll up your sleeves if you wish and dig into the actual codebase to ascertain the code quality as you do your due diligence.
Comparing Node.js schedulers
In this section, we will look at the most common job schedulers for the Node runtime environment. These include Agenda, Node-schedule, Node-cron, Bree, Cron, and Bull.
Agenda
Agenda is an MIT-licensed scheduler for Node. It was first published eight years ago and it is currently in version 4.2.1. It has 7.8k stars on GitHub and 62,214 weekly downloads on npm.
According to the GitHub commits graph, it is actively maintained. Given the number of releases, Agenda is a very mature package.
Agenda offers functionality for flexibly scheduling jobs using both cron and a more human-readable syntax. You will need to have a working MongoDB database to use Agenda as a job scheduler.
If you are not interested in downloading and installing MongoDB in your system, you can get a taste of this scheduler by creating a Docker container or using MongoDB’s cloud database Atlas. If you are not interested in using MongoDB, then probably this package is not a good option for you.
In the code snippet below, I have defined a simple job that logs to the console every minute:
const Agenda = require("agenda"); require("dotenv").config(); const databaseUri = `mongodb+srv://mawa:${process.env.PASSWORD}@test.nq88g.mongodb.net/jobs?retryWrites=true&w=majority`; const agenda = new Agenda({ db: { address: databaseUri, collection: "test-jobs" }, }); agenda.define("log", async (job) => { const { name } = job.attrs.data; console.log(`Hello ${name}! ${new Date().toLocaleTimeString()}`); }); (async function () { await agenda.start(); await agenda.every("one minute", "log", { name: "John Doe" }); })();
Agenda is a feature-rich scheduler. It offers promise-based APIs, job scheduling with configurable priority and concurrency, persistence of job results, and much more, which you can read about in their documentation.
Despite the rich features offered by Agenda, it also comes with multiple dependencies, as highlighted in the dependency graph below.
The publish bundle size of Agenda is 256KB and the install size is 18.8MB.
Node-schedule
This is another open-source, MIT-licensed package for scheduling jobs in Node. It is the second most popular Node scheduler considering the number of downloads on npm. With 43 versions released up until the current version 2.0.0, Node-schedule has had plenty of time to adapt and improve over the years.
Node-schedule is mainly for time-based rather than interval-based scheduling, though you can also use it flexibly. With Node-schedule, you can easily schedule a job to run at specific dates with an optional recurrence configuration.
Node-schedule offers you the flexibility of scheduling jobs using cron-style scheduling and date-based scheduling. With cron-style scheduling, you can pass a cron expression to specify when the job is triggered and the expression is parsed using cron-parser:
const nodeSchedule = require('node-schedule'); const job = nodeSchedule.scheduleJob('* * * * *', function(){ console.log('Job has been triggered at: ', new Date.toLocaleTimeString()); })
On the other hand, with date-based scheduling, you can pass an actual JavaScript date object to specify the exact date when a job is to be executed. It also provides an option to bind current data for use in the future, as illustrated in the code snippet below:
const nodeSchedule = require('node-schedule'); const birthDay = new Date(2021, 11, 21, 5, 30, 0); // Bae's birthday let birthDayGift = 🎁; const job = nodeSchedule.scheduleJob(birthDay, function(gift){ console.log('Happy Birthday Babe ', new Date.toLocaleDateString()); console.log(gift); }.bind(null, birthDayGift)); birthDayGift = null;
One limitation of Node-schedule, like most Node schedulers, is that the job will be triggered only when the script is running. If you are seeking to schedule a job that will persist even when the script is not running, consider using Cron. Similarly, it is not a good option if you want to persist the job between restarts.
Node-schedule has three first-level dependencies. You can see the dependencies of Node-schedule in the dependency graph below.
After running through Packagephobia, Node-schedule has publish size of 33.2KB and install size of 3.15MB.
Node-cron
Node-cron is an open-source, ISC-licensed package. First published 6 years ago, Node-cron is another mature, stable, and battle-tested Node scheduler.
It uses the GNU crontab expression for job scheduling. You can schedule Node-cron to trigger the job at a specific time or at intervals of time:
const nodeCron = require('node-cron'); nodeCron.schedule('* * * * * *', () => { // This job will run every second console.log(new Date().toLocaleTimeString()); })
Apart from the schedule
method for scheduling a job, Node-cron also exposes methods for validating a cron expression, starting, stopping, or destroying a scheduled job.
Though Node-cron will serve your needs if you are scheduling a simple job in Node or on the server-side, it won’t be of much use if you need a scheduler that runs in the browser or with support for worker threads.
Node-cron has only one first-level dependency.
Node-cron has publish and install size of 65.6KB and 5.26MB respectively according to Packagephobia.
Bree
Bree is another MIT-licensed JavaScript job scheduler. It runs both in Node and in the browser.
This package is pretty young compared to other schedulers mentioned above. It was published approximately nine months ago and about 72 versions have been published since then. The current version is 6.3.0 and has about 9,109 weekly downloads on npm.
However, despite being the youngest, Bree offers a lot more features than most of the other schedulers, and it is the only one that runs both in the browser and in Node. According to the documentation, Bree uses worker threads in Node and web workers in the browser to spawn sandboxed processes.
It provides support for concurrency, throttling, long-running jobs, and much more. Check the documentation for a full list of features.
The code snippet below shows how you can use Bree to schedule a simple job. scrape-data.js
, backup-database.js
, and send-email.js
are functions declared in the jobs folder at the root of the project directory:
const Bree = require('bree'); const jobs = [ { name: 'scrape-data', interval: 'every 1 minute'}, { name: 'backup-database', timeout: 'at 12:00 am'}, { name: 'send-email', timeout: '1m', interval: '5m'} ]; const bree = new Bree({ jobs }); bree.start();
This package has 13 first-level dependencies as illustrated in the dependency tree below.
The publish size is 1.38MB and the install size is 5.64MB after running it through Packagephobia.
Similarly, if you are thinking of running Bree on the client side, its minified and gzipped bundle size is 64.3KB after running the package through Bundlephobia:
Cron
This MIT-licensed Node scheduler should not be confused with Node-cron – be careful! Both Cron and Node-cron have the same repository name, node-cron
, on GitHub. By searching on GitHub, you might think one is a fork of the other.
This package was first published ten years ago with 37 versions published since then. It is currently in version 1.8.2 and has 909,120 weekly downloads on npm, the highest download count among the Node schedulers highlighted here.
You can use crontab syntax, '* * * * * *'
, to schedule when a job will be triggered. The expression extends the Unix pattern by adding a seconds option. If you don’t pass the seconds option, it defaults to zero.
It also offers the flexibility of passing a date object instead of the cron expression. Cron is very similar to Node-cron in terms of functionality, as you can see here:
const { CronJob } = require('cron'); const job = new CronJob('* * * * * *', function() { console.log('This job is triggered each second!'); }); job.start();
Cron has one first-level dependency and 1,516 dependents on npm. The dependencies of Cron are exactly the same as that of Node-cron as highlighted in the dependency tree below.
It has publish size of 69.3KB and install size of 5.25MB. You will notice that the publish size is only 3.7KB higher than that of Node-cron.
Bull
Bull is a Redis-based queue system for Node that requires a running Redis server. If you don’t want to use Redis, you will have to settle for the other schedulers. There are Redis hosting services that work well with Bull if you are not interested in installing it in your system.
Bull boasts the fastest and the most reliable Redis-based queue system for Node runtime environments. This MIT-licensed package published eight years ago and has 216,275 weekly downloads. The current version is 3.29.1, indicating its maturity and stability.
Bull offers features such as cron syntax-based job scheduling, rate-limiting of jobs, concurrency, running multiple jobs per queue, retries, and job priority, among others. For a full list of features, check the documentation.
The trivial example below will log “Hello World!” to the console after five seconds with the default Redis configuration:
const Queue = require('bull'); const helloWorldQueue = new Queue('hello-world-queue'); const data = { someData: 'Hello World!' }; const options = { delay: 5000, attempts: 3 }; helloWorldQueue.add(data, options); helloWorldQueue.process((job) => { console.log(job.data.someData); });
This package has ten first-level dependencies. You can check the subsequent level dependencies in the dependency graph below.
The package has publish size of 170KB and install size of 9.14MB.
Overview of job schedulers in Node
Some of the metrics and information highlighted in the previous sub-sections have been summarized in the table below:
Agenda | Node-cron | Node-schedule | Bree | Cron | Bull | |
---|---|---|---|---|---|---|
Install bundle size | 18.8MB | 5.26MB | 3.15MB | 5.64MB | 5.25MB | 9.14MB |
GitHub stars | 7.8k | 2k | 8k | 1.2k | 7.1k | 11.2k |
Test coverage | 80% | 100% | 95% | 100% | 81.3% | 94% |
Versions released | 85 | 19 | 43 | 72 | 37 | 136 |
Publish bundle size | 256KB | 65.6KB | 33.2KB | 1.38MB | 69.3KB | 170KB |
Minified + Gzipped bundle size | 336.8KB | 103.3KB | 31.1KB | 64.3KB | 94.4KB | 193.4KB |
Weekly downloads | 62,214 | 326,261 | 697,002 | 8,527 | 909,120 | 310,809 |
Documentation | Good | Good | Good | Good | Good | Good |
License | MIT | ISC | MIT | MIT | MIT | MIT |
All the packages we have looked at above are free, open-source, and well documented. The friendly licenses (MIT or ISC) give you the liberty to do pretty much anything with the codebase.
Node-cron, Node-schedule, and Cron are quite mature and stable. For simple job scheduling, you might want to go with one of them.
On the other hand, while Agenda and Bull are mature schedulers as well, they require MongoDB and Redis respectively.
Finally, the youngest of all the above schedulers is Bree. Despite its age, Bree is feature-rich. It runs in Node and the browser.
Conclusion
There are several job schedulers to pick from in the Node package ecosystem. Most of the packages have functionality that will meet your basic job scheduling needs, and your choice of a scheduler will largely depend on your use case.
If you intend to do simple job scheduling on the server side, you might want to explore Cron, Node-cron, and Node-schedule because they are simple, mature, popular, stable, and have friendly licenses. I would recommend using one of the three for simple jobs.
Agenda and Bull also offer simple job scheduling but with additional features and support for database persistence. You wouldn’t want all those bundles and dependencies in your application unless you are interested in using the additional features they offer.
On the other hand, if you are looking for a scheduler with extended functionalities such as support for worker threads or ability to run both in the browser and in Node (in addition to simple job scheduling), then Bree might be the best option for you.
Is there anything that I missed? Let me know in the comments section below.
200’s only
Monitor failed and slow network requests in production
Deploying a Node-based web app or website is the easy part. Making sure your Node instance continues to serve resources to your app is where things get tougher. If you’re interested in ensuring requests to the backend or third party services are successful, try LogRocket. 
LogRocket is like a DVR for web and mobile apps, recording literally everything that happens while a user interacts with your app. Instead of guessing why problems happen, you can aggregate and report on problematic network requests to quickly understand the root cause.
LogRocket instruments your app to record baseline performance timings such as page load time, time to first byte, slow network requests, and also logs Redux, NgRx, and Vuex actions/state. Start monitoring for free.
Plain Node.js job scheduling is not that convenient and reliable as OS scheduler solutions (`crontab` or `schtasks`). For that reason https://github.com/atmajs/cronbee combines both worlds – managing the jobs is done via CLI or API, and running them is left to the OS.
Node-cron fires multiple times sometimes, anyone knows how to solve that?
in my case use pm2 with cluster instance make multiple fires multiple times
Please check your number of instance. Because in every instance CRON gets executed, specially when you are running from server.
What do you think about croner?
https://github.com/Hexagon/croner