agenda vs bull vs node-schedule
Node.js Job Scheduling Libraries
agendabullnode-scheduleSimilar Packages:

Node.js Job Scheduling Libraries

Job scheduling libraries in Node.js are essential for managing and executing tasks at specific intervals or times. They help developers automate repetitive tasks, manage background jobs, and ensure that certain operations occur without manual intervention. These libraries provide various features, such as job persistence, concurrency control, and scheduling capabilities, allowing developers to focus on building applications without worrying about task management intricacies.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
agenda09,651295 kB2a month agoMIT
bull016,248309 kB147a year agoMIT
node-schedule09,22135 kB1713 years agoMIT

Feature Comparison: agenda vs bull vs node-schedule

Job Persistence

  • agenda:

    Agenda stores jobs in MongoDB, allowing for persistent job storage. This means that jobs can survive application restarts and can be queried or managed through the database, providing a reliable way to track job states and results.

  • bull:

    Bull uses Redis for job storage, which provides high performance and reliability. Jobs are stored in Redis, allowing for quick access and management, and ensuring that jobs are not lost even if the application crashes, thanks to Redis's persistence options.

  • node-schedule:

    Node-Schedule does not provide built-in job persistence; jobs are scheduled in memory. If the application restarts, scheduled jobs are lost. This makes it less suitable for long-running tasks that need to survive application restarts.

Concurrency Control

  • agenda:

    Agenda allows you to define concurrency levels for job processing, enabling you to control how many jobs are processed simultaneously. This feature is useful for managing resources and ensuring that your application does not become overwhelmed by too many concurrent tasks.

  • bull:

    Bull offers advanced concurrency control features, allowing you to set the maximum number of concurrent jobs that can be processed. This helps in managing system load and optimizing resource utilization, especially in high-throughput scenarios.

  • node-schedule:

    Node-Schedule does not inherently manage concurrency since it is primarily focused on scheduling rather than job processing. Developers need to implement their own concurrency management if needed.

Ease of Use

  • agenda:

    Agenda is designed to be simple and easy to use, with a straightforward API for scheduling jobs. Its integration with MongoDB makes it accessible for developers familiar with database operations, allowing quick setup and usage.

  • bull:

    Bull has a more complex API due to its rich feature set, but it provides extensive documentation and examples. While it may require a bit more effort to set up, its capabilities make it worth the investment for complex job management needs.

  • node-schedule:

    Node-Schedule is very easy to use, especially for those familiar with cron syntax. Its API is simple, allowing developers to quickly schedule jobs without needing to understand complex configurations.

Performance

  • agenda:

    Agenda's performance is generally good for small to medium workloads, but it may not scale as well as Bull for high-volume job processing due to its reliance on MongoDB for job management.

  • bull:

    Bull is optimized for performance and can handle a high volume of jobs efficiently. Its use of Redis allows for fast job processing and retrieval, making it suitable for applications with demanding job scheduling needs.

  • node-schedule:

    Node-Schedule is efficient for lightweight scheduling tasks but may not perform well under heavy loads or when many jobs are scheduled simultaneously, as it does not manage job queues.

Scheduling Flexibility

  • agenda:

    Agenda supports flexible scheduling options, allowing jobs to be scheduled at specific times, intervals, or using custom logic. This flexibility is beneficial for applications that require varied scheduling patterns.

  • bull:

    Bull allows for delayed jobs and priority queues, providing flexibility in job scheduling and execution order. This is particularly useful for applications that need to prioritize certain tasks over others.

  • node-schedule:

    Node-Schedule allows for cron-like scheduling, which is great for recurring tasks. However, it lacks the advanced scheduling features found in Bull, making it less versatile for complex job management.

How to Choose: agenda vs bull vs node-schedule

  • agenda:

    Choose Agenda if you need a lightweight job scheduler that integrates seamlessly with MongoDB and requires minimal setup. It is ideal for applications that need to schedule jobs based on a time interval and can benefit from MongoDB's persistence capabilities.

  • bull:

    Choose Bull if you require a robust and feature-rich job queue system that supports priority, delayed jobs, and concurrency. It is well-suited for applications that need to handle a high volume of jobs with reliability and performance, leveraging Redis for storage and management.

  • node-schedule:

    Choose Node-Schedule if you want a simple and straightforward scheduling library that allows you to schedule jobs using cron-like syntax. It is perfect for applications that need to run tasks at specific times or intervals without the overhead of a job queue.

README for agenda

Agenda

Agenda

A light-weight job scheduling library for Node.js

NPM Version NPM Downloads

Migrating from v5? See the Migration Guide for all breaking changes.

Agenda 6.x

Agenda 6.x is a complete TypeScript rewrite with a focus on modularity and flexibility:

  • Pluggable storage backends - Choose from MongoDB, PostgreSQL, Redis, or implement your own. Each backend is a separate package - install only what you need.

  • Pluggable notification channels - Move beyond polling with real-time job notifications via Redis, PostgreSQL LISTEN/NOTIFY, or other pub/sub systems. Jobs get processed immediately when saved, not on the next poll cycle.

  • Modern stack - ESM-only, Node.js 18+, full TypeScript with strict typing.

See the 6.x Roadmap for details and progress.

Features

  • Minimal overhead job scheduling
  • Pluggable storage backends (MongoDB, PostgreSQL, Redis)
  • TypeScript support with full typing
  • Scheduling via cron or human-readable syntax
  • Configurable concurrency and locking
  • Real-time job notifications (optional)
  • Sandboxed worker execution via fork mode
  • TypeScript decorators for class-based job definitions

Installation

Install the core package and your preferred backend:

# For MongoDB
npm install agenda @agendajs/mongo-backend

# For PostgreSQL
npm install agenda @agendajs/postgres-backend

# For Redis
npm install agenda @agendajs/redis-backend

Requirements:

  • Node.js 18+
  • Database of your choice (MongoDB 4+, PostgreSQL, or Redis)

Quick Start

import { Agenda } from 'agenda';
import { MongoBackend } from '@agendajs/mongo-backend';

const agenda = new Agenda({
  backend: new MongoBackend({ address: 'mongodb://localhost/agenda' })
});

// Define a job
agenda.define('send email', async (job) => {
  const { to, subject } = job.attrs.data;
  await sendEmail(to, subject);
});

// Start processing
await agenda.start();

// Schedule jobs
await agenda.every('1 hour', 'send email', { to: 'user@example.com', subject: 'Hello' });
await agenda.schedule('in 5 minutes', 'send email', { to: 'admin@example.com', subject: 'Report' });
await agenda.now('send email', { to: 'support@example.com', subject: 'Urgent' });

Official Backend Packages

PackageBackendNotificationsInstall
@agendajs/mongo-backendMongoDBPolling onlynpm install @agendajs/mongo-backend
@agendajs/postgres-backendPostgreSQLLISTEN/NOTIFYnpm install @agendajs/postgres-backend
@agendajs/redis-backendRedisPub/Subnpm install @agendajs/redis-backend

Backend Capabilities

BackendStorageNotificationsNotes
MongoDB (MongoBackend)Storage only. Combine with external notification channel for real-time.
PostgreSQL (PostgresBackend)Full backend. Uses LISTEN/NOTIFY for notifications.
Redis (RedisBackend)Full backend. Uses Pub/Sub for notifications.
InMemoryNotificationChannelNotifications only. For single-process/testing.

Backend Configuration

MongoDB

import { Agenda } from 'agenda';
import { MongoBackend } from '@agendajs/mongo-backend';

// Via connection string
const agenda = new Agenda({
  backend: new MongoBackend({ address: 'mongodb://localhost/agenda' })
});

// Via existing MongoDB connection
const agenda = new Agenda({
  backend: new MongoBackend({ mongo: existingDb })
});

// With options
const agenda = new Agenda({
  backend: new MongoBackend({
    mongo: db,
    collection: 'jobs'        // Collection name (default: 'agendaJobs')
  }),
  processEvery: '30 seconds', // Job polling interval
  maxConcurrency: 20,         // Max concurrent jobs
  defaultConcurrency: 5       // Default per job type
});

PostgreSQL

import { Agenda } from 'agenda';
import { PostgresBackend } from '@agendajs/postgres-backend';

const agenda = new Agenda({
  backend: new PostgresBackend({
    connectionString: 'postgresql://user:pass@localhost:5432/mydb'
  })
});

Redis

import { Agenda } from 'agenda';
import { RedisBackend } from '@agendajs/redis-backend';

const agenda = new Agenda({
  backend: new RedisBackend({
    connectionString: 'redis://localhost:6379'
  })
});

Real-Time Notifications

For faster job processing across distributed systems:

import { Agenda, InMemoryNotificationChannel } from 'agenda';
import { MongoBackend } from '@agendajs/mongo-backend';

const agenda = new Agenda({
  backend: new MongoBackend({ mongo: db }),
  notificationChannel: new InMemoryNotificationChannel()
});

Mixing Storage and Notification Backends

You can use MongoDB for storage while using a different system for real-time notifications:

import { Agenda } from 'agenda';
import { MongoBackend } from '@agendajs/mongo-backend';
import { RedisBackend } from '@agendajs/redis-backend';

// MongoDB for storage + Redis for real-time notifications
const redisBackend = new RedisBackend({ connectionString: 'redis://localhost:6379' });
const agenda = new Agenda({
  backend: new MongoBackend({ mongo: db }),
  notificationChannel: redisBackend.notificationChannel
});

This is useful when you want MongoDB's proven durability and flexible queries for job storage, but need faster real-time notifications across multiple processes.

API Overview

Defining Jobs

// Simple async handler
agenda.define('my-job', async (job) => {
  console.log('Processing:', job.attrs.data);
});

// With options
agenda.define('my-job', async (job) => { /* ... */ }, {
  concurrency: 10,
  lockLimit: 5,
  lockLifetime: 10 * 60 * 1000, // 10 minutes
  priority: 'high'
});

Defining Jobs with Decorators

For a class-based approach, use TypeScript decorators:

import { JobsController, Define, Every, registerJobs, Job } from 'agenda';

@JobsController({ namespace: 'email' })
class EmailJobs {
  @Define({ concurrency: 5 })
  async sendWelcome(job: Job<{ userId: string }>) {
    console.log('Sending welcome to:', job.attrs.data.userId);
  }

  @Every('1 hour')
  async cleanupBounced(job: Job) {
    console.log('Cleaning up bounced emails');
  }
}

registerJobs(agenda, [new EmailJobs()]);
await agenda.start();

// Schedule using namespaced name
await agenda.now('email.sendWelcome', { userId: '123' });

See Decorators Documentation for full details.

Scheduling Jobs

// Run immediately
await agenda.now('my-job', { userId: '123' });

// Run at specific time
await agenda.schedule('tomorrow at noon', 'my-job', data);
await agenda.schedule(new Date('2024-12-25'), 'my-job', data);

// Run repeatedly
await agenda.every('5 minutes', 'my-job');
await agenda.every('0 * * * *', 'my-job'); // Cron syntax

Job Control

// Cancel jobs (removes from database)
await agenda.cancel({ name: 'my-job' });

// Disable/enable jobs globally (by query)
await agenda.disable({ name: 'my-job' });  // Disable all jobs matching query
await agenda.enable({ name: 'my-job' });   // Enable all jobs matching query

// Disable/enable individual jobs
const job = await agenda.create('my-job', data);
job.disable();
await job.save();

// Progress tracking
agenda.define('long-job', async (job) => {
  for (let i = 0; i <= 100; i += 10) {
    await doWork();
    await job.touch(i); // Report progress 0-100
  }
});

Stopping / Draining

// Stop immediately - unlocks running jobs so other workers can pick them up
await agenda.stop();

// Drain - waits for running jobs to complete before stopping
await agenda.drain();

// Drain with timeout (30 seconds) - for cloud platforms with shutdown deadlines
const result = await agenda.drain(30000);
if (result.timedOut) {
    console.log(`${result.running} jobs still running after timeout`);
}

// Drain with AbortSignal - for external control
const controller = new AbortController();
setTimeout(() => controller.abort(), 30000);
await agenda.drain({ signal: controller.signal });

Use drain() for graceful shutdowns where you want in-progress jobs to finish their work.

Events

agenda.on('start', (job) => console.log('Job started:', job.attrs.name));
agenda.on('complete', (job) => console.log('Job completed:', job.attrs.name));
agenda.on('success', (job) => console.log('Job succeeded:', job.attrs.name));
agenda.on('fail', (err, job) => console.log('Job failed:', job.attrs.name, err));

// Job-specific events
agenda.on('start:send email', (job) => { /* ... */ });
agenda.on('fail:send email', (err, job) => { /* ... */ });

Custom Backend

For databases other than MongoDB, PostgreSQL, or Redis, implement AgendaBackend:

import { AgendaBackend, JobRepository } from 'agenda';

class SQLiteBackend implements AgendaBackend {
  readonly repository: JobRepository;
  readonly notificationChannel = undefined; // Or implement NotificationChannel

  async connect() { /* ... */ }
  async disconnect() { /* ... */ }
}

const agenda = new Agenda({
  backend: new SQLiteBackend({ path: './jobs.db' })
});

See Custom Backend Driver for details.

Documentation

Related Packages

Official Backend Packages:

Tools:

License

MIT