Manager implementation for Cron Jobs in Node.js

Viacheslav Rudnev
3 min readNov 1, 2023

--

Cron jobs are a powerful tool to implement scheduled tasks in your Node.js application. One of the useful packages for this purpose is the cron package.

Simple manager implementation

Our manager should have a simple interface to start and stop tasks

const { CJ } = require('cron');

class CronTasks {
/**
* @type {Object.<string, CJ>}
*/
tasks = {};

constructor() {
if (!CronTasks._instance) {
CronTasks._instance = this;
}
return CronTasks._instance;
}

/**
* @return {CronTasks}
*/
static getInstance() {
if (!this._instance) {
new CronTasks();
}
return this._instance;
}

/**
* @param {Object.<string, CJ>} tasks
* @param {boolean} run
*/
addTasks(tasks, run = true) {
Object.keys(tasks).forEach((taskName) => {
if (taskName in this.tasks) {
return;
}
this.tasks[taskName] = tasks[taskName];

if (run) {
this.start(taskName);
}
});
}

/**
* @param {string|Array.<string>} taskNames
*/
start(taskNames) {
if (Array.isArray(taskNames)) {
return taskNames.forEach((taskName) => {
this.start(taskName);
});
}
if (taskNames in this.tasks) {
this.tasks[taskNames].start();
}
}

/**
* @param {string|Array.<string>} taskNames
*/
stop(taskNames) {
if (Array.isArray(taskNames)) {
return taskNames.forEach((taskName) => {
this.stop(taskName);
});
}
if (taskNames in this.tasks) {
this.tasks[taskNames].stop();
}
}

startAll() {
Object.values(this.tasks).forEach((task) => task.start());
}

stopAll() {
Object.values(this.tasks).forEach((task) => task.stop());
}

/**
* @return {string[]} task names
*/
getTaskNames() {
return Object.keys(this.tasks);
}
}

module.exports = CronTasks;

Job implementation

A task can look like this:

const { CronJob, CronTime } = require('cron');

/**
* @callback CronJobCallback
*/

/**
* @param {CronTime|string} time
* @param {CronJobCallback} task
* @return {CronJob|CJ}
* @constructor
*/
const CustomCronTask = (time = '@minutely', task = () => {}) =>
new CronJob(time, async () => {
await task();
});

module.exports = CustomCronTask;

Or:

const { CronJob, CronTime } = require('cron');
const lockAccountService = require('../lock-account.service');

/**
* @param {CronTime|string} time
* @return {CronJob|CJ}
* @constructor
*/
const LockAccountsCronTask = (time = '@minutely') =>
new CronJob(time, async () => {
await lockAccountService.lockAdminAccount();
await lockAccountService.lockUserAccount();
});

module.exports = LockAccountsCronTask;

How to use

To add a cron job we need to use something like this (by default any added job is automatically started, to disable this behavior just pass false as the second parameter to addTasks function):

try {
CronTasks.getInstance().addTasks({
LockAccountsCronTask: LockAccountsCronTask('*/5 1-4 * * *')
});
} catch (e) {
console.log('Cron ERROR:', e.message);
}

This manager allows easy control of any task for example via Express.Js API endpoints

const getJobs = async(req, res) => {
return res.json({ jobs: CronTasks.getInstance().getTaskNames() });
}

const stopJob = async(req, res) => {
const { job } = req.query;
const taskNames = CronTasks.getInstance().getTaskNames();
if (!taskNames.includes(job)) {
return res.json({ message: `Cron job '${job}' don't register` });
}

CronTasks.getInstance().stop(job);

return res.json({ message: `Cron job '${job}' is stoped` });
}

const startJob = async(req, res) => {
const { job } = req.query;
const taskNames = CronTasks.getInstance().getTaskNames();
if (!taskNames.includes(job)) {
return res.json({ message: `Cron job '${job}' don't register` });
}

CronTasks.getInstance().start(job);

return res.json({ message: `Cron job '${job}' is started` });
}

module.exports = {
getJobs,
stopJob,
startJob
};

Conclusion

During this article, we implemented a simple cron jobs manager, a single cron job, and looked at how to add jobs to this manager, start/stop jobs.

This simple implementation is very flexible and can be easily modified to add, for example, a job status display.

--

--