In this article, you will learn how to schedule a job in Node using node-cron.

Node-cron is a handy npm package which you can use to schedule jobs to run at specific times or intervals. It is most suitable for scheduling repetitive jobs such as email notifications, file downloads, and database backups.

Even if you are not interested in scheduling a job in Node, you may still find the knowledge you gain from this article about cron syntax very useful.

For example, Github Actions uses cron syntax when scheduling a workflow to run at a specific time. Similarly, cloud platforms such as Google Cloud require cron syntax when describing job schedules.

Node-cron is written for node in pure JavaScript and it is based on GNU crontab syntax. Though it is based on crontab, our focus in this article will be on learning node-cron and cron syntax.

For more about cron, crontab, and how they are used in Unix-like operating systems, you can take a look at this Wikipedia article on the topic (but you don't have to know it to follow along with this article).

What you will learn in this article

By the end of this article, you will be able to do the following:

  • Explain the cron syntax
  • Schedule jobs using node-cron

Prerequisites

Before proceeding, make sure you have the prerequisites completed that are outlined below.

  • You need to have the JavaScript runtime environment Node installed on your machine.
  • You should have at least a basic understanding of JavaScript and Node. If you are a total beginner with Node and JavaScript, you can ask questions on the freeCodeCamp forum if you get stuck. We shall be happy to help.

How to Use node-cron to Schedule a Job

As I already mentioned above, node-cron was written for Node and is distributed via npm. After installation using the command npm i node-cron, it must be required into the project like any other Node package:

const nodeCron = require("node-cron");

To schedule a job, you need to invoke the nodeCron.schedule method with two arguments. There is a third optional argument that you can pass to the method for additional configuration.

Below is the function signature for the nodeCron.schedule method.

nodeCron.schedule(expression, function, options);

The code snippet below is an example of how you can invoke the schedule method.

const job = nodeCron.schedule("* * * * * *", function jobYouNeedToExecute() {
  // Do whatever you want in here. Send email, Make  database backup or download data.
  console.log(new Date().toLocaleString());
});

The first argument you need to pass to nodeCron.schedule is the cron expression. You use this expression to specify the time (or times) at which the job should be executed.

This expression should be in the * * * * * * format. You can replace each * field with an appropriate number (or characters where possible) so that the expression describes the time at which the job should be executed.

If you pass "* * * * * *" without replacing any *, like in the above example, the job gets executed every second. For a detailed explanation of how to come up with a cron expression, read the "How to understand cron expressions" sub-section below.

The second argument is a function and it is the job that gets executed when the expression in the first argument ticks.

You can do whatever you want in this function. You can send an email, make a database backup, or download data. This function gets executed when the current system time is the same as the time provided in the first argument. In the above example, I am just printing the current date.

And the third argument is an optional configuration object for job scheduling. I didn't pass the third argument in the above example since it is optional.

Below is an example of what the third argument looks like.

{
   scheduled: false,
   timezone: "America/Sao_Paulo"
}

By default scheduled is true. If you set it to false, you will have to schedule the job by invoking the start method on the job object. job is the object returned by a call to the schedule method.

job.start();

The default timezone we use is for the system on which the job is scheduled. You can pass a different timezone if you wish.

How to Understand Cron Expressions

The cron expression, which is the first argument to schedule, is a string that takes the form "* * * * * *". We use it to describe the time at which the job should be executed. Each * in the expression is a field and you can see the field represented by each * in the illustration below.

"* * * * * *"
 | | | | | |
 | | | | | |
 | | | | | day of week
 | | | | month
 | | | day of month
 | | hour
 | minute
 second(optional)

As you can see from the above illustration, the first field is the second field, the second field is the minute field, and the third is the hour field, and so on.

The table below shows the fields and their corresponding allowed values:

Field Allowed values
second 0 - 59
minute 0 - 59
hour 0 - 23
day of month 1 - 31
month 1 - 12 or names
day of week 0 - 7 or names, 0 and 7 refer to sunday

The job is executed when the second, minute, hour, and month fields match the current time, and when at least one of the two day fields (day of month, or day of week) match the current time. – crontab documentation

There are different ways to populate the fields in a cron expression. Each field in a Node expression can be populated using single integer values, a range of values, multiple values separated by commas, step values, or using names (as explained in the sub-sections below).

How to Use Single Integer Values to Populate a Chron Expression

You can replace each asterisk with a single integer value in the allowed range of values.

For example, passing "30 20 * * * *" will make node-cron run your job at the thirtieth second of the twentieth minute of each hour. Since you didn't specify a value for the hour field, node-cron interprets * to mean every hour. The same applies to the day of the month field, and so on.

const job = nodeCron.schedule("30 20 * * * *", () => {
  console.log(new Date().toLocaleString());
});

Similarly, passing "30 5 13 * * *" will run your task at 1:05:30pm every day.

const job = nodeCron.schedule("30 5 13 * * *", () => {
  console.log(new Date().toLocaleString());
});

How to Use a Range of Values to Populate Chron Expressions

You an also use ranges of numbers to populate your chron expressions. A range refers to two numbers separated by the - character. The end values are part of the range.

For example, if the hour field is set to2-4, it specifies execution at hours 2, 3, and 4.

const job = nodeCron.schedule("* 2-4 3 * *", () => {
  console.log(new Date().toLocaleString());
});

In the above code snippet, I have excluded the optional second field. It will execute your job every minute from 2 am to 4 am on the third day (because the day of the month field has a value of 3) of each month.

How to Use Multiple Values to Populate Chron Expressions

You can also pass multiple values separated by commas or a range of values separated by commas.

For example, passing 2,3,4 as the value of the minute field will execute your job at minutes 2, 3, and 4.

const job = nodeCron.schedule("2,3,4 * * * *", () => {
  console.log(new Date().toLocaleString());
});

In the above code snippet, I have again excluded the optional second field. It will execute your job at the first, second, and third minutes of each hour.

How to Use Step Values to Populate Chrone Expressions

You can use step values with ranges. Following a range with /<number> skips the number's value through the range.

For example, using 0-8/2 in the hour field will execute the code at 0,2,4,6 and 8 hours. You can also use step values with *. For example */3 executes every three hours.

const job = nodeCron.schedule("*/2 * * * *", () => {
  console.log(new Date().toLocaleString());
});

In the above code snippet, the job will be executed every two minutes. Once again I've omitted the optional second field.

How to Use Names to Populate Chron Expressions

For the month and day of the week fields, you can use names. These can be short or long names. For example January or Jan.

const job = nodeCron.schedule("* * * January,September Sunday", () => {
  console.log(new Date().toLocaleString());
});

Once again I have omitted the optional second field. The job will run every minute on Sundays in January and September. You can also use short names like Jan, Sep.

That is all you need to know about the basics of cron syntax. In the next section, you will implement what you have learned to schedule a simple job.

There is a handy tool called crontab guru which can interpret crontab expressions for you. If you enter an expression, it will validate the expression and tell you when the job will be executed. You can use it if you are not sure of the expression.

How to Schedule a Job using node-cron

In this section, you will apply what you have learned in the previous sections. You will build a simple app that scrapes world population data from worldometers site and logs it to the console.

When you navigate to the worldometers world population page, you will notice the current world population changing rapidly. You will schedule a job that will scrape the data and print it on the terminal.

In a real app, you will normally save it to a database. Follow the steps below to build the app.

Step 1 - How to Create a Directory

In this step, you will create a directory for your project and navigate to it. Open the terminal and run the command below to create a directory called learn-node-cron. The name of the directory doesn't matter. You can give it a different name if you wish.

mkdir learn-node-cron

You should see the learn-node-cron folder created after running the above command successfully. You can open the folder in your favorite text editor.
In the next step, you will initialize the project.

Step 2 - How to Initialize the Project

In this step, you will initialize the project by running the command below on the terminal.

npm init -y

After successfully running the above command, you should be able to see the package.json file created at the root of the project directory.

Step 3 - How to Install Dependencies

In this step, you will install the project dependencies by running the command below on the terminal.

npm i node-cron puppeteer ora chalk

The above installation will take a bit of time, so just be patient. After successfully installing the above dependencies, you should see them in the package.json file under the dependencies field.

node-cron is the most important dependency here because it is what this article is all about.

We'll use puppeteer to scrape data from a web page. According to the documentation puppeteer is:

A Node library that provides a high-level API to control Chrome or Chromium over the DevTools Protocol. Puppeteer runs headless by default but can be configured to run full (non-headless) Chrome or Chromium - puppeteer documentation

If the above statement doesn't make sense to you, there is a nice article on toptal that explains puppeteer, headless browsers, and why they are necessary. It is still okay if you are not interested in puppeteer. This article is about node-cron and how to use it to schedule a job.

ora is a simple npm package that we will use for displaying messages and a spinner on the terminal as we scrape the data. This will provide a better user experience.

chalk is another npm package that we'll use for displaying colorful messages on the terminal.

In the next step, you will implement the cron job.

Step 4 - How to Implement the Cron Job

In this step, you will implement a simple cron job. Create a new JavaScript file by running the command below:

touch app.js

Successfully running the above command will create an app.js file at the root of the project. Copy and paste the code below in the file you have just created:

const nodeCron = require("node-cron");
const puppeteer = require("puppeteer");
const ora = require("ora");
const chalk = require("chalk");

const url = "https://www.worldometers.info/world-population/";

async function scrapeWorldPopulation() {
  // Log a message on the terminal as the scheduled job starts
  // We are using chalk to make the message on the terminal look colorful
  console.log(chalk.green("Running scheduled job"));
  // Launch a loading spinner with an appropriate message on the terminal
  // It provides a good user experience as the scraping process takes a bit of time
  const spinner = ora({
    text: "Launcing puppeteer",
    color: "blue",
    hideCursor: false,
  }).start();

  try {
    // This will help us compute the duration of the job later
    const date = Date.now();
    // Launch puppeteeer
    const browser = await puppeteer.launch();
    // Change the message on the terminal as we launch
    // a new headless browser page
    spinner.text = "Launching headless browser page";
    // Launch a new headless browser page
    const newPage = await browser.newPage();
    // Change the message on the terminal as we navigate
    // to the URL of the page we are scraping
    spinner.text = "Navigating to URL";
    // Navigate to the URL of the page we are scraping. This takes a bit of time
    // You can change the timeout to an appropriate value if you wish otherwise
    // we wait until the page loads
    await newPage.goto(url, { waitUntil: "load", timeout: 0 });

    // Change the message on the terminal as we start scraping the page
    spinner.text = "Scraping page";
    // Start scraping the page
    // If world population is 7,876,395,914 then digitGroups will be
    // ["7", "876", "395", "914"]
    const digitGroups = await newPage.evaluate(() => {
      const digitGroupsArr = [];
      // For selecting span elements containing digit groups
      const selector =
        "#maincounter-wrap .maincounter-number .rts-counter span";
      const digitSpans = document.querySelectorAll(selector);
      // Loop through the digit spans selected above
      digitSpans.forEach((span) => {
        if (!isNaN(parseInt(span.textContent))) {
          digitGroupsArr.push(span.textContent);
        }
      });
      return JSON.stringify(digitGroupsArr);
    });
    // Change the message on the terminal since we are about
    // to close the headless browser
    spinner.text = "Closing headless browser";
    // Close the headless browser
    await browser.close();
    // Print success message with duration it took to scrape the data in ms
    spinner.succeed(`Page scraping successfull after ${Date.now() - date}ms`);
    // Remove the spinner from the terminal
    spinner.clear();
    // Print world population on the terminal if scraping is successful
    console.log(
      chalk.yellow.bold(`World population on ${new Date().toISOString()}:`),
      chalk.blue.bold(JSON.parse(digitGroups).join(","))
    );
  } catch (error) {
    // Print failed on the terminal if scraping is unsuccessful
    spinner.fail({ text: "Scraping failed" });
    // Remove the spinner from the terminal
    spinner.clear();
    // Print the error message on the terminal
    console.log(error);
  }
}
// Schedule a job to run every two minutes
const job = nodeCron.schedule("*/2 * * * *", scrapeWorldPopulation);

In the above code snippet, we required all the dependencies we needed at the top of the file. They include node-cron, puppeteer, chalk, and ora. We are scraping the data from https://www.worldometers.info/world-population/, so I assigned it to the url variable.

Since our job is to scrape data from a site, I have named the function responsible for executing our job scrapeWorldPopulation.

I have tried my best to give self-explanatory variable names and commented on almost every line of code. You should be able to follow what is happening in the scrapeWorldPopulation function.

Since this article is about job scheduling, I won't dive into puppeteer. You can also implement a different job if you wish.

I scheduled the job to run every two minutes by invoking the nodeCron.schedule method at the bottom.

You can run the command node app.js on the terminal to schedule the job. You should see the job run every two minutes. This is what I see on my terminal after running node app.js.

Terminal

Conclusion

In this article, you learned crontab syntax and how to implement a cron job in Node using node-cron.

There are several projects you can try out to learn cron syntax, for example:

  • scheduling a job to fetch the latest COVID-19 vaccination coverage for your country and logging the output to the console
  • fetching trending hashtags from Twitter (this might need an API key), or
  • getting the latest news headlines at intervals of time and so on.

References