• Skip to primary navigation
  • Skip to main content
  • Skip to primary sidebar

Abhishek Tanwar

I build web & mobile experiences

  • HOME
  • BLOG
  • CONTACT ME
  • DISCLAIMER & COPYRIGHTS
You are here: Home / Building APIs / How to implement background jobs in Node.js

How to implement background jobs in Node.js

March 25, 2018 by Abhishek Tanwar

Now that you are here, I believe you want to understand how to implement background jobs in Node.js. I took me some time to figure it out but I will put down the understanding and solution for you.

Problem Statement

To be able to run background jobs and take action while the main application can continue to work.

The Application

Let’s consider an application that takes request for report generation which will take some time and you cant have user waiting for the same. So there are two parts of the application. One a server side which takes user requests for new reports and other a background worker which will process the same in the background without disturbing the main application.

I gave the implementation design some thoughts and came up with 2 approaches. One with MongoDB and one with Redis. Let’s look at them.

MongoDB Approach

Server Module

This server module is simple Rest API written in Express and Mongo driver which just put the job request in Mongo and moves on. Mongo being a JS oriented database is quite fast when handling and maintaining JS objects from the application. So here is the code.

const express = require('express')
const bodyParser = require('body-parser')
const app = express()
const url = "mongodb://192.168.99.100:32774/"
const dbName = 'offlinereport'

var MongoClient = require('mongodb').MongoClient
var db
var batchStart = 1
const batchMax = 5


var addRequest = function(request){
 if(batchStart > batchMax)
 batchStart = 1

return new Promise(function(resolve, reject) {

var myObj = {...request, batch: batchStart, submitted: new Date().getTime()}


 // use mongo
 MongoClient.connect(url, function(err, db) {
 if (err) reject(err);
 var dbo = db.db(dbName)
 dbo.collection("requests").insertOne(myObj, function(err, res) {
 if (err) reject(err);
 db.close();

batchStart++;

resolve(res)
 })
 }) 
 });
}

app.use(bodyParser.json())

app.put('/add', function (req, res) {
 addRequest(req.body).then(function(result){
 res.send(req.body) 
 }) 
})


app.listen(3000, () => {
 console.log('listening on 3000')
})

Worker Module

This worker module is node program which just polls Mongo for new updates and once it reads for its own batch, it will mark the same as processed continue to the job. The trick here is that the workers need to know which batch they are in so while starting the program we provide a command line argument for the batch number. So bottom line, it picks all pending jobs from DB and processes them one by one.

var MongoClient = require('mongodb').MongoClient
var url = "mongodb://192.168.99.100:32774/"
const {ObjectId} = require('mongodb')
const batch = process.argv[2]
const dbName = 'offlinereport'
var counter = 1

var getRecords = function() {
 return new Promise(function(resolve, reject) {
 MongoClient.connect(url, function(err, db) {
 if (err) reject(err);

var dbo = db.db(dbName)
 var query = { batch: 1, status: 'N' }
 var sort = { submitted: 1}


 dbo.collection("requests")
 .find(query)
 .sort(sort)
 .limit(1)
 .toArray(function(err, result) {
 
 if (err) throw err
 resolve(result)
 db.close();
 });
 });
 });
}

var updateRecordStatus = function(oid, status) {
 return new Promise(function(resolve, reject) {
 MongoClient.connect(url, function(err, db) {
 
 if (err) reject(err)

var dbo = db.db(dbName)
 var myquery = {_id: ObjectId(oid) }
 var newvalues = { $set: { status: status } }
 //console.log(myquery, newvalues)
 dbo.collection("requests")
 .updateOne(myquery, newvalues, function(err, res) {
 if (err) reject({})
 //console.log(res.result)
 resolve({})
 db.close();
 });
 });
 });
}


function checkForUpdates () {
 console.log(new Date(), 'check-for-updates started')
 
 // get new data to process
 getRecords().then(function(result) {
 // mark record as processing
 //console.log(result.length)
 if(result.length > 0) {
 var oid = result[0]._id
 updateRecordStatus(oid, 'P').then(function (result){
 console.log(new Date(), 'marked record as processing')
 // process the record
 // processing done, update data is processed
 updateRecordStatus(oid, 'D').then(function (result){
 console.log(new Date(), 'marked record as processed')
 console.log(new Date(), 'check-for-updates completed')
 console.log(new Date(), 'going to sleep..')
 counter ++
 setTimeout(checkForUpdates, 10000)
 })
 })
 }
 else{
 console.log(new Date(), 'no new records, next time..')
 counter ++
 setTimeout(checkForUpdates, 2000)
 }
 }) 
}

checkForUpdates()

Redis Approach

Server Module

This server module is simple Rest API written in Express and Redis Library Bee-Queue which just put the job request in the queue and moves on.

const express = require(‘express’)
const Queue = require(‘bee-queue’);
const bodyParser = require(‘body-parser’)
const app = express()
const requests = new Queue(‘requests’, {
redis: {
host: ‘192.168.99.100’,
port: 32775,
db: 0,
options: {}
},
isWorker: false
});

var addRequest = function(request){

var myObj = {…request, submitted: new Date().getTime()}
const job = requests.createJob(myObj);

return job.timeout(3000).retries(2).save();

}

app.use(bodyParser.json())

app.put(‘/add’, function (req, res) {
addRequest(req.body).then(function(result){
console.log(‘job submitted’,result.id)
res.send(req.body)
})
})

app.listen(3000, () => {
console.log(‘listening on 3000’)
})

 

Worker Module

This is straight forward with the bee-queue providing a right method for thsi job.

const Queue = require('bee-queue');
const bodyParser = require('body-parser')
const requests = new Queue('requests', {
 redis: {
 host: '192.168.99.100',
 port: 32775,
 db: 0,
 options: {}
 },
 isWorker: true,
 removeOnSuccess: true,
});

requests.process(function (job, done) {
 console.log(new Date(), `processing started for ${job.id}`);
 console.log(new Date(), `processing completed for job ${job.id}`);
 return done(null, job.data);
});

console.log(new Date(), 'worker started');

It is up to you which approach you choose based on your requirement. My personal preference is Redis. Let me know your views.

 

Share this:

  • Facebook
  • X

Like this:

Like Loading...

Filed Under: Building APIs, Fullstack Tagged With: nodejs background jobs, nodejs mongo, nodjs, redis, redis queues

Primary Sidebar

  • Behance
  • Email
  • Facebook
  • GitHub
  • LinkedIn

Popular Posts

How To Integrate React in JSP Application
SaaS, PaaS, IaaS
XQuery highlighting in Notepad++
Standard Design Patterns – Applied to JAVA
XQDT Installation on Eclipse JUNO

Categories

  • Building APIs
  • Building Mobile Apps
  • Building Web Apps
  • Coding Challenges
  • Extensions
  • Fullstack
  • Patterns
  • Tips & Tricks
  • Uncategorized
  • VSCode

Tags

2022 vscode extensions Abstract Factory alienware amazing vscode extensions apple challenges coding coding-challenges Design Patterns dota2 dota2 items dynamic web service call dynamic webservice invocation enterprise integration express flex helpful vscode extensions how to implement mfa in nodejs increasing productivity using vscode integration pattern macro message channel message endpoint message pattern message router message translator messaging mfa offline action processing offline actions in struts pipes and filters PRG in struts react Steam struts System Emulator threads in struts utilities vscode vscode customization vscode extensions vscode productivity extensions xquery xquery 1.0 xquery design principles

Recent Posts

  • 15+ VSCode Extensions To Improve Your Productivity
  • Productivity and Quality Extensions for ReactJS in VSCode..
  • My goto VSCode plugins for 2022…
  • How to enable MFA for your application in Node.JS?
  • Structuring Express Application – How I do it?

Categories

  • Building APIs (8)
  • Building Mobile Apps (1)
  • Building Web Apps (7)
  • Coding Challenges (3)
  • Extensions (1)
  • Fullstack (6)
  • Patterns (8)
  • Tips & Tricks (41)
  • Uncategorized (43)
  • VSCode (2)

Tags

2022 vscode extensions Abstract Factory alienware amazing vscode extensions apple challenges coding coding-challenges Design Patterns dota2 dota2 items dynamic web service call dynamic webservice invocation enterprise integration express flex helpful vscode extensions how to implement mfa in nodejs increasing productivity using vscode integration pattern macro message channel message endpoint message pattern message router message translator messaging mfa offline action processing offline actions in struts pipes and filters PRG in struts react Steam struts System Emulator threads in struts utilities vscode vscode customization vscode extensions vscode productivity extensions xquery xquery 1.0 xquery design principles

Archives

  • August 2023 (1)
  • March 2022 (1)
  • February 2022 (1)
  • November 2020 (1)
  • April 2020 (2)
  • May 2019 (1)
  • April 2019 (1)
  • March 2019 (1)
  • December 2018 (1)
  • July 2018 (4)
  • June 2018 (3)
  • March 2018 (1)
  • May 2016 (1)
  • December 2015 (1)
  • May 2015 (1)
  • April 2015 (2)
  • March 2015 (1)
  • December 2014 (2)
  • November 2014 (2)
  • October 2014 (1)
  • August 2014 (2)
  • July 2014 (1)
  • April 2014 (1)
  • March 2014 (1)
  • February 2014 (2)
  • January 2014 (1)
  • December 2013 (4)
  • November 2013 (4)
  • October 2013 (3)
  • September 2013 (4)
  • August 2013 (2)
  • July 2013 (5)
  • June 2013 (9)
  • May 2013 (6)
  • April 2013 (2)
  • March 2013 (2)
  • January 2013 (1)
  • November 2012 (2)
  • HOME
  • BLOG
  • CONTACT ME
  • DISCLAIMER & COPYRIGHTS

Copyright © 2025

%d