Scheduler
@veloxts/scheduler provides expressive, fluent task scheduling with cron expressions.
Installation
Section titled “Installation”pnpm add @veloxts/schedulerQuick Start
Section titled “Quick Start”import { schedulerPlugin, task } from '@veloxts/scheduler';
app.register(schedulerPlugin({ timezone: 'UTC', tasks: [ task('cleanup-sessions', async (ctx) => { await ctx.db.session.deleteMany({ where: { expiresAt: { lt: new Date() } }, }); }) .daily() .at('03:00') .build(), ],}));import { schedulerPlugin, task } from '@veloxts/scheduler';
app.register(schedulerPlugin({ timezone: process.env.SCHEDULER_TIMEZONE || 'UTC', tasks: [ task('cleanup-sessions', cleanupSessions) .daily() .at('03:00') .when(() => process.env.SCHEDULER_ENABLED === 'true') .withoutOverlapping(10) .build(), ], onTaskError: (task, ctx, error) => { errorTracker.capture(error, { task: task.name }); },}));Schedule Frequencies
Section titled “Schedule Frequencies”task('name', handler) // Time-based .everyMinute() .everyFiveMinutes() .everyFifteenMinutes() .everyThirtyMinutes() .hourly() .hourlyAt(15) // At :15 past the hour .daily() .dailyAt('09:00') .weekly() .weeklyOn('monday', '09:00') .monthly() .monthlyOn(1, '00:00') // 1st of month at midnight
// Day constraints .weekdays() // Monday-Friday .weekends() // Saturday-Sunday
// Custom cron .cron('*/5 * * * *') // Every 5 minutes
.build()Task Options
Section titled “Task Options”task('sync-data', syncData) .hourly() .timezone('America/New_York') // Task-specific timezone .withoutOverlapping() // Skip if still running .withoutOverlapping(30) // Max lock time in minutes .when(() => isMainServer()) // Conditional execution .skip(() => isMaintenanceMode()) // Skip condition .onSuccess((ctx, duration) => { console.log(`Completed in ${duration}ms`); }) .onFailure((ctx, error, duration) => { notifySlack(`Task failed: ${error.message}`); }) .build()Scheduler API
Section titled “Scheduler API”// Access scheduler from contextconst scheduler = ctx.scheduler;
// Check statusscheduler.isRunning();
// Get tasksconst tasks = scheduler.getTasks();const task = scheduler.getTask('cleanup-tokens');
// Manual executionawait scheduler.runTask('cleanup-tokens');
// Next run timeconst nextRun = scheduler.getNextRun('cleanup-tokens');Production Deployment
Section titled “Production Deployment”Why Single Instance?
Section titled “Why Single Instance?”Unlike other ecosystem packages, the scheduler doesn’t require Redis. However, it must only run on one server instance to prevent duplicate task execution.
| Scenario | Result |
|---|---|
| 3 instances, all with scheduler | Each task runs 3x |
| 3 instances, 1 with scheduler | Each task runs 1x ✓ |
Configuration
Section titled “Configuration”app.register(schedulerPlugin({ timezone: process.env.SCHEDULER_TIMEZONE || 'UTC', tasks: [ task('cleanup', cleanup) .daily() .at('02:00') .when(() => process.env.SCHEDULER_ENABLED === 'true') .withoutOverlapping(10) .build(), ], onTaskStart: (task, ctx) => { console.log(`Starting: ${task.name}`); }, onTaskComplete: (task, ctx, duration) => { metrics.timing(`scheduler.${task.name}`, duration); }, onTaskError: (task, ctx, error) => { Sentry.captureException(error, { tags: { task: task.name } }); },}));Environment Variables
Section titled “Environment Variables”SCHEDULER_ENABLED=true # Only set on scheduler instanceSCHEDULER_TIMEZONE=UTC # Default timezoneGraceful Shutdown
Section titled “Graceful Shutdown”Allow running tasks to complete before shutdown:
process.on('SIGTERM', async () => { await app.close(); // Waits for scheduler to stop process.exit(0);});Running as Separate Process
Section titled “Running as Separate Process”For better isolation, run scheduler separately from your web server:
import { createScheduler, task } from '@veloxts/scheduler';
const scheduler = createScheduler({ timezone: process.env.SCHEDULER_TIMEZONE || 'UTC', tasks: [ task('cleanup', cleanup).daily().at('02:00').build(), task('reports', sendReports).weekdays().at('09:00').build(), ],});
scheduler.start();
process.on('SIGTERM', async () => { await scheduler.stop(); process.exit(0);});# Run as separate processnode scheduler.jsProduction Checklist
Section titled “Production Checklist”- Single instance - Enable scheduler on ONE server only
- Graceful shutdown - Handle SIGTERM to let tasks complete
- Error monitoring - Track failures with
onTaskError - Overlap prevention - Use
withoutOverlapping()for long tasks - Explicit timezone - Set timezone for predictable execution
Standalone Usage
Section titled “Standalone Usage”Use scheduler outside of Fastify context:
import { createScheduler, task } from '@veloxts/scheduler';
const scheduler = createScheduler({ timezone: 'UTC', tasks: [ task('my-task', () => console.log('Running!')) .everyMinute() .build(), ],});
scheduler.start();
// Later...await scheduler.stop();Related Content
Section titled “Related Content”- Queue - Background jobs
- Ecosystem Overview - All packages