How I schedule jobs for my pet project with Agenda JS

Hung Tran Nguyen
3 min readDec 18, 2019

While playing with my small Express app a bit (https://medium.com/@trannguyenhung011086/first-try-with-express-37055e4e75fc), I started to diving more in NodeJS events method to make a simple and manageable email sending scheduler.

Below is how I develop my code from using the native NodeJS eventEmitter to Agenda JS methods.

— -

First of all is the mailer service containing methods:
- send active email when user register

// mailer.jsconst config = require(‘../config’);
const mailer = require(‘@sendgrid/mail’);
mailer.setApiKey(config.sendgrid);module.exports = {
sendActiveEmail: async ({ username, email, url }) => {
try {
const msg = {
to: email,
from: ‘learn-svelte@example.com’,
subject: ‘Your active link’,
text: `Hi ${username}, please click the following url to activate your account: ${url}.\nThank you!`,
html: `<p>Hi <strong>${username}</strong>,</p>
<p>Please click <a href=${url}>the following url</a> to activate your account.</p>
<p>Thank you!</p>`,
};
const send = await mailer.send(msg);
console.log(‘Active email is sent to ‘ + email);
return send;
} catch (err) {
console.error(‘Cannot send email!\n’, err);
}
},
};

Then come the user service which is used for the Express user routes. Here I define ‘register’ event for the eventEmitter method.

// userService.jsmodule.exports = {

createUser: async ({ username, email, password }) => {
const payload = { username, email, password };
await validateRegister(payload);
payload.password = await bcrypt.hash(password, 10);
let newUser = await UserModel.create(payload);
const send = myEmitter.emit(‘register’, {
username,
email,
url: newUser.activeLink,
});
if (!send) myEmitter.emit(‘error’);
return newUser;
},
}
,

Then I call mailer service in event subcribers.

// event.jsconst EventEmitter = require(‘events’);
const myEmitter = new EventEmitter();
const mailer = require(‘../services/mailer’);myEmitter.on(‘register’, async ({ username, email, url }) => {
await mailer.sendActiveEmail({ username, email, url });
});
module.exports = myEmitter;

— -

The above code work quite well for the case of sending active email right after user registers. But what about if I want to send a welcome email in 1 minutes after they click the active link?

First I think of using setTimeout method but it is not an easy way to scale later with more scheduled jobs. That was when I found out about Agenda JS which is a lightweight solution for such purpose.

You can check how to install Agenda as instructed at https://github.com/agenda/agenda. Please note, it requires to connect to Mongo database to work.

Then I started modifying my code a bit as below.

// agenda.jsconst Agenda = require(‘agenda’);
const config = require(‘../config’);
const mailer = require(‘../services/mailer’);const agenda = new Agenda({
db: { address: config.database, collection: ‘agendaJobs’ },
});
agenda
.on(‘ready’, () => console.log(‘Agenda started!’))
.on(‘error’, () => console.log(‘Agenda connection error!’));
agenda.define(‘register’, async job => {
const { username, email, url } = job.attrs.data;
await mailer.sendActiveEmail({ username, email, url });
});
agenda.define(‘welcome’, async job => {
const { username, email } = job.attrs.data;
await mailer.sendWelcomeEmail({ username, email });
});
agenda.start();module.exports = agenda;

Similar to the native event subscriber method, here I define jobs for Agenda to consume. Then in another file, I define methods for Agenda to publish.

// scheduler.jsconst agenda = require(‘./agenda’);module.exports = {
scheduleActiveEmail: async ({ username, email, url }) => {
await agenda.schedule(‘in 1 second’, ‘register’, {
username,
email,
url,
});
},
scheduleResendActiveEmail: async ({ username, email, url }) => {
await agenda.schedule(‘in 1 second’, ‘resend’, {
username,
email,
url,
});
},
scheduleWelcomeEmail: async ({ username, email }) => {
await agenda.schedule(‘in 30 seconds’, ‘welcome’, { username, email });
},
};

Next, I use the new Agenda publisher methods in user service.

// userService.jsmodule.exports = {
createUser: async ({ username, email, password }) => {
const payload = { username, email, password };
await validateRegister(payload);
payload.password = await bcrypt.hash(password, 10);
let newUser = await UserModel.create(payload);
await scheduler.scheduleActiveEmail({
username,
email,
url: newUser.activeLink,
});
return newUser;
},
activeUser: async ({ userId, uuid }) => {
const user = await getUserById(userId);

await scheduler.scheduleWelcomeEmail({
username: user.username,
email: user.email,
});
user.active = true;
user.welcome = true;
await user.save();
return { userId: user._id, active: user.active };
},
};

— -

Finally, after the jobs are processed, I can query of job data stored in AgendaJobs collection on Mongo database.

In summary, Agenda JS is a suitable solution for simple management of scheduling jobs in NodeJS rather than using the native setTimeout or traditional cron jobs.

It even provides a dashboard solution which I will continue to learn more and write about it soon :)

--

--

Hung Tran Nguyen

From translator -> Manual QC Engineer -> Automation QA Engineer -> NodeJS Developer -> yearning to learn more