agenda vs cron vs later vs node-cron vs node-schedule
Node.js でのタスクスケジューリングライブラリの比較
agendacronlaternode-cronnode-schedule類似パッケージ:

Node.js でのタスクスケジューリングライブラリの比較

agendacronlaternode-cronnode-schedule はすべて Node.js 環境で定期実行やスケジュールされたタスクを管理するためのライブラリです。これらのライブラリは、Cron 式による繰り返しジョブの設定や、特定日時での一回限りの実行といった基本機能を提供しますが、永続化、依存関係、柔軟なスケジュール記述、およびアーキテクチャ設計において大きく異なります。例えば、agenda は MongoDB を使ってジョブを永続化し、再起動後も状態を維持できる一方、cronnode-cron はメモリ上でのみ動作し、プロセス終了時にスケジュールが失われます。later は独自のスケジュール DSL を提供し、Cron 式以外の柔軟な指定が可能ですが、メンテナンスが停止されています。

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

3 年

GitHub Starsランキング

統計詳細

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

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

Node.js で定期処理やスケジュール実行を行うには、いくつかの選択肢があります。それぞれのライブラリは「いつ」「何を」「どう実行するか」の設計思想が異なり、用途に応じて適切なものを選ぶ必要があります。この記事では、5つの主要なライブラリを実際のコードとともに比較し、現場で役立つ判断基準を示します。

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

最初に重要な注意点です。later公式に非推奨(deprecated) となっています。npm ページおよび GitHub リポジトリ(bunkat/later)は 2016 年以降更新されておらず、アーカイブ済みです。新規プロジェクトでは絶対に使用せず、代わりに cronnode-schedule を検討してください。


🗓️ 基本的な Cron 式による繰り返し実行

多くのライブラリは Unix 標準の Cron 式(例:'0 9 * * *' = 毎日9時)をサポートしています。以下に各ライブラリでの実装例を示します。

cron

const CronJob = require('cron').CronJob;

const job = new CronJob('0 9 * * *', () => {
  console.log('毎日9時に実行');
}, null, true, 'Asia/Tokyo');

node-cron

const cron = require('node-cron');

cron.schedule('0 9 * * *', () => {
  console.log('毎日9時に実行');
}, {
  scheduled: true,
  timezone: 'Asia/Tokyo'
});

node-schedule

const schedule = require('node-schedule');

schedule.scheduleJob('0 9 * * *', () => {
  console.log('毎日9時に実行');
});

agenda

agenda は Cron 式を repeatEvery で使うことができますが、内部的には human-readable 文字列(例:'3 minutes')もサポートします。

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

agenda.define('daily task', (job) => {
  console.log('毎日9時に実行');
});

(async () => {
  await agenda.start();
  await agenda.every('0 9 * * *', 'daily task');
})();

💡 注意:later は非推奨のため、コード例は省略します。


💾 ジョブの永続化:再起動後もタスクを継続できるか?

プロセスがクラッシュまたは再起動した際に、未完了のジョブを復元できるかどうかは、運用上の大きな違いです。

agenda:MongoDB で永続化

agenda は MongoDB にジョブを保存するため、サーバー再起動後もスケジュールが維持されます。また、失敗したジョブのリトライやロック機構も備えています。

// MongoDB にジョブが保存される
await agenda.schedule('tomorrow at 9am', 'send email', { to: 'user@example.com' });

その他(cron, node-cron, node-schedule):メモリ上のみ

これらはすべて メモリ上でのみ スケジュールを管理します。プロセスが終了すると、登録済みのジョブはすべて失われます。

// node-cron の例:プロセス終了でスケジュール消失
const task = cron.schedule('*/5 * * * *', () => {
  // 5分ごとの処理
});
// process.exit() で task は完全に消える

この特性から、cron/node-cron/node-schedule は「アプリケーション起動中にだけ有効な一時タスク」に適しています。一方、agenda は「システム全体で信頼性が求められるバッチ処理」に適しています。


🧩 柔軟なスケジュール指定:Cron 式以外の選択肢

Cron 式だけでは表現できないスケジュール(例:「毎月第2水曜日」)を扱いたい場合があります。

node-schedule:RecurrenceRule

node-scheduleRecurrenceRule オブジェクトで詳細なルールを定義できます。

const schedule = require('node-schedule');

const rule = new schedule.RecurrenceRule();
rule.dayOfWeek = 3; // 水曜日(0=日曜)
rule.hour = 9;
rule.minute = 0;

// ただし「第2水曜日」は直接指定不可 → 別途ロジックが必要
schedule.scheduleJob(rule, () => {
  console.log('毎週水曜日9時');
});

実際には「第2水曜日」を正確に表現するには追加計算が必要ですが、node-schedule は Date オブジェクトやカスタムルールで柔軟に対応可能です。

later(非推奨):独自 DSL

later'every 2 weeks on Wednesday at 9:00' のような自然言語風の記述をサポートしていましたが、現在は使用できません。

その他:Cron 式のみ

cronnode-cronagenda は基本的に Cron 式に依存しており、複雑なカスタムロジックは自前で実装する必要があります。


🛠️ API 設計と使いやすさ

コールバック vs Promise

  • cron:コールバック中心。async/await と組み合わせにくい。
  • node-cron:Promise 対応。async 関数を直接渡せる。
  • node-schedule:コールバックだが、戻り値として Job オブジェクトを返し、動的制御が可能。
  • agenda:Promise/async 対応。モダンな非同期処理に最適。
// node-cron:async 対応
cron.schedule('* * * * *', async () => {
  await fetchData();
});

// agenda:async 対応
agenda.define('fetch data', async (job) => {
  await fetchData();
});

ジョブの動的制御(開始・停止・キャンセル)

  • cronjob.start() / job.stop()
  • node-crontask.start() / task.stop()
  • node-schedulejob.cancel()
  • agendaagenda.cancel() で条件に基づき削除可能
// node-schedule:動的キャンセル
const job = schedule.scheduleJob('*/10 * * * *', () => {
  console.log('10分ごと');
});

// ある条件で停止
if (needStop) job.cancel();

🏗️ アーキテクチャと依存関係

ライブラリ外部依存永続化本番向き
agendaMongoDB 必須
cronなし△(小規模)
node-cronなし△(小規模)
node-scheduleなし△(中規模)
laterなし(非推奨)
  • 小規模スクリプトや開発用node-cron が軽量で使いやすい。
  • 中規模アプリで柔軟なスケジュールnode-schedule がバランス良い。
  • 本番環境で信頼性が必要agenda 一択(MongoDB 導入が前提)。

📌 まとめ:どう選ぶべきか?

  • 永続化と信頼性が必須agenda(MongoDB 使用可なら)
  • 軽量でシンプルな Cron 処理node-cron(Promise 対応で現代的)
  • 柔軟なスケジュールルールが必要node-schedule
  • 古いコードベースで既に使っているcron(ただし新規プロジェクトでは node-cron 推奨)
  • 新規プロジェクトlater は絶対に使わない

最終的に、あなたのアプリケーションが「プロセス寿命を超えてジョブを維持する必要があるか?」という一点で、agenda かそれ以外かが決まります。その上で、API の使いやすさやスケジュールの柔軟性で微調整するのがベストプラクティスです。

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

  • agenda:

    agenda はジョブの永続化とクラスタ対応が必要な本番環境向けに最適です。MongoDB をバックエンドとして使い、再起動後も未完了のジョブを復元できるため、信頼性が求められるシステム(例:メール配信、データ同期)に適しています。ただし、MongoDB への依存があるため、インフラ要件が増える点に注意が必要です。

  • cron:

    cron はシンプルで軽量な Cron 実装を求めている場合に適しています。標準的な Cron 式をサポートし、コールバックベースの API で簡単に使えるため、小規模なスクリプトやプロトタイプ開発に向いています。ただし、ジョブの永続化や詳細な制御機能はなく、プロセス終了時にスケジュールが消える点に留意してください。

  • later:

    later は非推奨(deprecated)のパッケージであり、新規プロジェクトでの使用は避けてください。最後の更新は 2016 年以降なく、公式 GitHub リポジトリもアーカイブされています。代替として cronnode-schedule を検討すべきです。

  • node-cron:

    node-cron は標準的な Cron 式を使い、Promise 対応や TypeScript サポートなど現代的な API を備えています。軽量で依存が少なく、シンプルな定期タスク(例:監視スクリプト、キャッシュ更新)に最適です。ただし、ジョブの永続化や高度なスケジュールロジックはサポートしていません。

  • node-schedule:

    node-schedule は Cron 式だけでなく、Date オブジェクトや RecurrenceRule による柔軟なスケジュール指定が可能です。たとえば「毎月第2火曜日」のような複雑なルールも表現できます。永続化機能はありませんが、スケジュールの動的変更やキャンセルが容易で、中規模アプリケーションでの一時的なタスク管理に適しています。

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