agenda vs cron vs later vs node-cron
Node.jsにおけるスケジューリングライブラリの比較
agendacronlaternode-cron類似パッケージ:

Node.jsにおけるスケジューリングライブラリの比較

agendacronlaternode-cron はすべて Node.js 環境でタスクをスケジュール実行するためのライブラリですが、それぞれ設計思想や用途が異なります。agenda は MongoDB を使って永続化されたジョブキューを提供し、失敗時の再試行や優先度制御など高度な機能を備えています。一方、cronnode-cron は Unix cron の構文に似た形式で定期実行を定義でき、軽量かつシンプルなユースケースに向いています。later は柔軟なスケジュール記述(例えば「毎月第2火曜日」など)を可能にする一方、開発が停止しており新規プロジェクトでの使用は推奨されません。これらのライブラリは、単純なタイマーから堅牢なジョブ管理システムまで、さまざまなニーズに対応しています。

npmのダウンロードトレンド

3 年

GitHub Starsランキング

統計詳細

パッケージ
ダウンロード数
Stars
サイズ
Issues
公開日時
ライセンス
agenda09,651295 kB21ヶ月前MIT
cron08,914161 kB254ヶ月前MIT
later02,418-9810年前MIT
node-cron03,245221 kB339ヶ月前ISC

Node.js スケジューリングライブラリ徹底比較:agenda vs cron vs later vs node-cron

Node.js で定期実行や遅延実行のタスクを扱う際、いくつかの選択肢があります。agendacronlaternode-cron は代表的な npm パッケージですが、それぞれが異なる問題を解決するために作られています。この記事では、実際のコード例を交えながら、各ライブラリの特徴と使いどころを解説します。

⚠️ まず確認:非推奨パッケージについて

later は公式に非推奨(deprecated)となっています。npm ページおよび GitHub リポジトリには「This project is no longer maintained」と明記されており、新規プロジェクトでの使用は避けるべきです。以降の比較では参考のために言及しますが、実用上は他の3つに絞って検討すべきです。

🗂️ アーキテクチャの違い:永続化 vs メモリ内

agenda:MongoDB による永続化ジョブキューシステム

agenda は単なるタイマーではなく、MongoDB を使ってジョブを永続化する本格的なジョブキューシステムです。プロセスが再起動しても未完了のジョブが失われず、再試行やロック機構も備えています。

// agenda: MongoDB にジョブを保存
const Agenda = require('agenda');

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

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

await agenda.start();

// 5分後に1回だけ実行
await agenda.schedule('in 5 minutes', 'send email', { to: 'user@example.com' });

cron / node-cron / later:メモリ内スケジューラー

これらはすべて Node.js プロセスのメモリ内で動作し、プロセス終了時にスケジュールが消えます。永続化や再試行機能はありません。

// cron: メモリ内スケジューラー
const CronJob = require('cron').CronJob;

new CronJob('0 */2 * * *', () => {
  console.log('2時間ごとに実行');
}, null, true);

// node-cron: 同様にメモリ内
const cron = require('node-cron');

cron.schedule('0 */2 * * *', () => {
  console.log('2時間ごとに実行');
});

// later: 非推奨だが参考までに
const later = require('later');
later.date.UTC();
const sched = later.parse.cron('0 */2 * * *');
later.setInterval(() => console.log('2時間ごと'), sched);

🕒 スケジュール記述の柔軟性

cronnode-cron:標準 cron 構文

どちらも Unix cron に似た5フィールド構文(分 時 日 月 曜日)を使います。node-cron は秒フィールドをオプションでサポートします。

// cron: 標準5フィールド
new CronJob('0 9 * * 1-5', task); // 平日9時

// node-cron: 秒を含む6フィールドも可
cron.schedule('0 0 9 * * 1-5', task); // 同じく平日9時

later:自然言語に近い柔軟な記述(非推奨)

later は「毎月第2火曜日」や「毎週金曜日の15時〜17時」など、非常に複雑なスケジュールを記述できますが、非推奨のためここでは紹介に留めます。

agenda:自然言語または Date オブジェクト

agendahuman-interval ライブラリを使って「in 10 minutes」や「at 3:00pm tomorrow」のような自然言語でスケジュールを指定できます。

// agenda: 自然言語によるスケジュール
await agenda.schedule('tomorrow at 3pm', 'backup');
await agenda.schedule('in 2 hours', 'cleanup');

🌍 タイムゾーン対応

cron:限定的

cron はコンストラクタで timeZone オプションを指定できますが、内部的には moment-timezone に依存しており、設定がやや面倒です。

// cron: タイムゾーン指定
new CronJob('0 9 * * *', task, null, true, 'Asia/Tokyo');

node-cron:ネイティブサポート

node-cronoptions.timezone で直接タイムゾーンを指定でき、より直感的です。

// node-cron: タイムゾーン指定
cron.schedule('0 9 * * *', task, { timezone: 'Asia/Tokyo' });

agenda:グローバル設定可能

agenda はインスタンス作成時に defaultConcurrencyprocessEvery と共に defaultLockLifetime を設定できますが、タイムゾーンはスケジュール文字列に組み込むか、Date オブジェクトで明示的に指定します。

// agenda: Date オブジェクトでタイムゾーン考慮
const jst = new Date();
jst.setHours(21, 0, 0, 0); // JST 21時
await agenda.schedule(jst, 'nightly-task');

later:手動設定(非推奨)

later.date.UTC()later.date.localTime() で調整可能ですが、非推奨のため詳細は割愛します。

🛠️ 実行制御とライフサイクル

cron:基本的な start/stop

CronJob インスタンスには start()stop() メソッドがありますが、API はやや冗長です。

const job = new CronJob('*/10 * * * * *', task);
job.start();
// ...
job.stop();

node-cron:シンプルな戻り値制御

schedule() はスケジュールオブジェクトを返し、そこから start() / stop() が呼び出せます。

const task = cron.schedule('*/10 * * * * *', () => {});
task.start();
// ...
task.stop();

agenda:ジョブの動的管理

agendacancel()jobs()purge() などのメソッドで、実行中のジョブを動的に管理できます。

// 実行中のジョブをキャンセル
await agenda.cancel({ name: 'send email' });

// 特定のジョブを取得
const jobs = await agenda.jobs({ name: 'backup' });

📊 ユースケース別:どのライブラリを選ぶべきか?

シナリオ1:シンプルな監視スクリプト(例:1分ごとのAPIチェック)

  • 推奨: node-cron
  • 理由: 軽量でタイムゾーン指定も簡単。cron よりも直感的なAPI。
const cron = require('node-cron');
cron.schedule('* * * * *', checkApi, { timezone: 'UTC' });

シナリオ2:メール送信やデータバックアップなどの重要なバッチ処理

  • 推奨: agenda
  • 理由: プロセスクラッシュ後もジョブが失われず、再試行やロック機構で信頼性が確保できる。
await agenda.schedule('0 2 * * *', 'backup-database');

シナリオ3:複雑なカスタムスケジュール(例:毎月最終金曜日)

  • 推奨: node-cron + 手動判定
  • 理由: later は非推奨のため、cron で毎日実行し、関数内で条件をチェックするのが現実的。
// 毎日実行し、最終金曜日かどうかを判定
cron.schedule('0 9 * * *', () => {
  if (isLastFriday(new Date())) doTask();
});

📌 まとめ:各ライブラリの特徴

ライブラリ永続化再試行タイムゾーン複雑スケジュール非推奨
agenda✅ (MongoDB)⚠️ (手動)⚠️ (自然言語)
cron⚠️ (moment依存)
later⚠️ (手動)
node-cron

💡 最終アドバイス

  • 軽量でシンプルな定期実行node-croncron よりも使いやすい)
  • 信頼性が求められるジョブ管理agenda(MongoDB が使える前提)
  • later は絶対に使わない → 非推奨であり、セキュリティリスクや互換性問題の原因になり得る

あなたのプロジェクトが「一度失敗したら終わり」でよいのか、「確実に実行されなければならない」のか — その判断が、どのライブラリを選ぶべきかを決めます。

選び方: agenda vs cron vs later vs node-cron

  • agenda:

    agenda は、MongoDB をバックエンドに持つ堅牢なジョブキューシステムが必要な場合に最適です。ジョブの永続化、再試行、優先度、並列処理制御といった機能が求められる本格的なバッチ処理や非同期タスク管理に適しています。ただし、MongoDB への依存があるため、データベースを使いたくない軽量な用途には不向きです。

  • cron:

    cron は、Unix cron 構文に慣れている開発者向けのシンプルなスケジューラーです。軽量で依存が少なく、短時間の定期タスク(例:1分ごとの監視)に適しています。ただし、タイムゾーンサポートが限定的で、複雑なスケジュール(例:「毎月最終金曜日」)には対応していません。

  • later:

    later は非常に柔軟なスケジュール記述が可能でしたが、公式に開発が終了しており、npm ページにも「deprecated」と明記されています。新規プロジェクトでは絶対に使用せず、代わりに cronnode-cron を検討してください。既存プロジェクトで使っている場合は、移行を検討すべきです。

  • node-cron:

    node-croncron と同様の cron 構文を使いながら、タイムゾーン指定やより直感的な API を提供します。例えば start()stop() メソッドでスケジュールの制御が可能です。軽量かつ使いやすく、特にタイムゾーンを考慮した定期実行が必要な場合に cron よりも優れた選択肢です。

agenda のREADME

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