How to use Agenda scheduler in Node.js
Agenda is a lightweight job scheduling library for Node.js that stores jobs in MongoDB, providing persistence across restarts. As the creator of CoreUI with over 10 years of Node.js experience since 2014, I’ve used Agenda for email campaigns, report generation, and recurring maintenance tasks in production applications. The standard approach defines job processors, schedules jobs with cron expressions or intervals, and monitors execution with event listeners. This provides durable scheduled task execution with minimal overhead.
Install Agenda and connect it to MongoDB.
npm install agenda
const { Agenda } = require('agenda')
const agenda = new Agenda({
db: {
address: 'mongodb://localhost:27017/agenda',
collection: 'jobs'
}
})
agenda.define('send welcome email', async (job) => {
const { userId, email } = job.attrs.data
console.log(`Sending welcome email to ${email}`)
await sendEmail(email, 'Welcome!', 'Welcome to our platform.')
})
;(async () => {
await agenda.start()
await agenda.schedule('in 5 minutes', 'send welcome email', {
userId: '123',
email: '[email protected]'
})
})()
async function sendEmail(to, subject, body) {
console.log(`Email sent to ${to}: ${subject}`)
}
agenda.define registers a job processor. agenda.start() begins processing. agenda.schedule creates a one-time job. Job data is persisted to MongoDB, surviving server restarts.
Recurring Jobs with Cron
Schedule jobs that repeat on a cron schedule.
const { Agenda } = require('agenda')
const agenda = new Agenda({
db: { address: 'mongodb://localhost:27017/agenda' }
})
agenda.define('daily report', async (job) => {
console.log('Generating daily report...')
await generateReport()
})
agenda.define('cleanup old sessions', async (job) => {
console.log('Cleaning up sessions older than 30 days...')
await cleanupSessions()
})
;(async () => {
await agenda.start()
await agenda.every('0 9 * * 1-5', 'daily report')
await agenda.every('0 2 * * *', 'cleanup old sessions')
console.log('Scheduler started')
})()
async function generateReport() {
await new Promise(resolve => setTimeout(resolve, 2000))
}
async function cleanupSessions() {
await new Promise(resolve => setTimeout(resolve, 500))
}
agenda.every schedules recurring jobs. The first argument is a cron expression. 0 9 * * 1-5 runs at 9 AM on weekdays. 0 2 * * * runs at 2 AM daily. Jobs persist in MongoDB so schedules survive restarts.
Job Priorities and Concurrency
Control how many jobs run simultaneously.
const agenda = new Agenda({
db: { address: 'mongodb://localhost:27017/agenda' },
maxConcurrency: 20,
defaultConcurrency: 5,
lockLimit: 10
})
agenda.define('high priority task', { priority: 'high', concurrency: 1 }, async (job) => {
console.log('Running high priority task')
})
agenda.define('bulk email', { priority: 'low', concurrency: 3 }, async (job) => {
const { batch } = job.attrs.data
console.log(`Sending bulk email batch ${batch}`)
})
priority controls job order in the queue. concurrency limits simultaneous instances of one job type. maxConcurrency caps total concurrent jobs. Tune these to prevent overloading your server.
Handling Job Failures
Monitor and retry failed jobs.
agenda.define('process payment', {
attempts: 3,
backoff: { type: 'fixed', delay: 60000 }
}, async (job) => {
const { paymentId } = job.attrs.data
if (Math.random() < 0.2) {
throw new Error('Payment gateway timeout')
}
console.log(`Payment ${paymentId} processed`)
})
agenda.on('fail:process payment', (err, job) => {
console.error(`Payment job failed: ${err.message}`)
console.error('Job data:', job.attrs.data)
})
agenda.on('complete:process payment', (job) => {
console.log(`Payment job completed for ${job.attrs.data.paymentId}`)
})
attempts retries failed jobs automatically. backoff adds delay between retries. Event listeners track failures and completions. Failed jobs stay in MongoDB for inspection and manual retry.
Best Practice Note
This is the same Agenda pattern we use in CoreUI backend services for scheduled maintenance and notification tasks. Always run agenda.start() on application boot and agenda.stop() on graceful shutdown to avoid orphaned locks. Use agenda.cancel() to remove obsolete jobs when changing schedules. Monitor the jobs collection in MongoDB to track execution history. For high-throughput scenarios, consider Bull with Redis instead - Agenda is excellent for moderate workloads where MongoDB is already in your stack. Define job names as constants to avoid typos across your codebase.



