Search code examples
javascriptnode.jscronnode-schedule

Call API at scheduled time


I was trying to run an API call at a scheduled time. I researched through sites and found this package called node-schedule from npmjs. This is working as expected by calling the code at required time. The issue I am having is :

Suppose I have a list of times eg: ["10:00","11:00","13:00"]

Once I start the server, it will get executed at required times. But what if I want to change the time list dynamically?

Exactly what I am trying to do:

  1. Call API and get times from Database
  2. Setup cron-schedule for each of these times.
  3. Dynamically adding new time to database

What I want: Dynamically add this newly added time to the cron-schedule

index.js

const express = require('express');
const schedule = require('node-schedule');
const app = express();
const port = 5000;

var date = new Date(2019, 5, 04, 14, 05, 20);// API call here
var j = schedule.scheduleJob(date, function(){
  console.log('The world is going to end today.');
});

app.get('/test', (req, res) => {
  var date = new Date(2019, 5, 04, 14, 11, 0); // Will call API here
  var q = schedule.scheduleJob(date, function(){
   console.log('Hurray!!');
  });
  res.send('hello there');
});

app.listen(port, () => console.log(`Example app listening on port ${port}!`));

The code written above is what I have and its pretty messed up. What I am conveying there is that, while running the index.js file API is called and the cron-schedule gets executed. Now if there is some new values added to DB I want to rerun this.

Rerunning index.js is another option I have, but I don't think its the right thing to do. The next option I have in mind is to call another endpoint which is mentioned as /test above, which would eventually run the cron again.

Please let me know some suggestions or some sort of solution to this so I can rectify my mistakes.


Solution

  • With this code I think you could do what you want, although you must adapt it to your needs in terms of defining the functions that will execute the tasks or if you need to specify the times in which they will be executed in another way (setting specific days of the week, for example).

    var times = [];
    var tasks = [];
    
    function addTask(time, fn) {
        var timeArr = time.split(':');
        var cronString = timeArr[1] + ' ' + timeArr[0] + ' * * *';
        // according to https://github.com/node-schedule/node-schedule#cron-style-scheduling
        var newTask = schedule.scheduleJob(cronString, fn);
    
        // check if there was a task defined for this time and overwrite it
        // this code would not allow inserting two tasks that are executed at the same time
        var idx = times.indexOf(time);
        if (idx > -1) tasks[idx] = newTask;
        else {
            times.push(time);
            tasks.push(newTask);
        }
    }
    
    function cancelTask(time) {
        // https://github.com/node-schedule/node-schedule#jobcancelreschedule
        var idx = times.indexOf(time);
        if (idx > -1) {
            tasks[idx].cancel();
            tasks.splice(idx, 1);
            times.splice(idx, 1);
        }
    }
    
    function init(tasks) {
        for (var i in tasks){
            addTask(i, tasks[i]);
        }
    }
    
    init({
        "10:00": function(){ console.log("It's 10:00"); },
        "11:00": function(){ console.log("It's 11:00"); },
        "13:00": function(){ console.log("It's 13:00"); }
    });
    
    app.post('/addTask', (req, res) => {
        if (!req.body.time.match(/^(0[0-9]|1[0-9]|2[0-3]|[0-9]):[0-5][0-9]$/)) {
            // regex from https://stackoverflow.com/a/7536768/8296184
            return res.status(400).json({'success': false, 'code': 'ERR_TIME'});
        }
        function fn() {
            // I suppose you will not use this feature just to do console.logs 
            // and not sure how you plan to do the logic to create new tasks
            console.log("It's " + req.body.time);
        }
        addTask(req.body.time, fn);
        res.status(200).json({'success': true});
    });