Advanced job scheduling with cron syntax, repeatable patterns, and time-based execution control.
Create jobs that execute on recurring schedules using cron expressions or intervals.
// Repeatable jobs are created using the repeat option in add() method
interface RepeatOptions {
/** Cron expression for scheduling */
cron?: string;
/** Repeat every N milliseconds */
every?: number;
/** Timezone for cron expressions */
tz?: string;
/** End date to stop repetition */
endDate?: Date | string | number;
/** Maximum number of repetitions */
limit?: number;
/** Starting count for iteration tracking */
count?: number;
/** Start date for cron-based jobs */
startDate?: Date | string | number;
}
interface CronRepeatOptions extends RepeatOptions {
cron: string;
startDate?: Date | string | number;
}
interface EveryRepeatOptions extends RepeatOptions {
every: number;
}Usage Examples:
const Queue = require('bull');
const taskQueue = new Queue('scheduled tasks');
// Cron-based repeatable job - daily at 9 AM
const dailyReport = await taskQueue.add('daily-report',
{ reportType: 'sales' },
{
repeat: {
cron: '0 9 * * *', // 9 AM every day
tz: 'America/New_York'
}
}
);
// Interval-based repeatable job - every 5 minutes
const healthCheck = await taskQueue.add('health-check',
{ service: 'api' },
{
repeat: {
every: 5 * 60 * 1000 // 5 minutes in milliseconds
}
}
);
// Weekly job with end date
const weeklyCleanup = await taskQueue.add('cleanup',
{ type: 'temp-files' },
{
repeat: {
cron: '0 2 * * 0', // 2 AM every Sunday
endDate: new Date('2024-12-31'),
tz: 'UTC'
}
}
);
// Limited repetition - run 10 times
const limitedJob = await taskQueue.add('backup',
{ database: 'users' },
{
repeat: {
cron: '0 0 * * *', // Daily at midnight
limit: 10 // Stop after 10 executions
}
}
);Common cron patterns for scheduling repeatable jobs.
// Cron expression format: second minute hour day month dayOfWeek
// Note: Bull uses 6-field cron (includes seconds), but 5-field also supported
const cronExamples = {
// Every minute
everyMinute: '* * * * *',
// Every 5 minutes
every5Minutes: '*/5 * * * *',
// Every hour at minute 30
everyHourAt30: '30 * * * *',
// Daily at 8:30 AM
dailyAt830AM: '30 8 * * *',
// Every weekday at 9 AM
weekdaysAt9AM: '0 9 * * 1-5',
// Every Monday at 10 AM
mondaysAt10AM: '0 10 * * 1',
// First day of every month at midnight
monthlyFirstDay: '0 0 1 * *',
// Every 15 minutes during business hours (9 AM - 5 PM)
businessHours: '*/15 9-17 * * 1-5',
// Twice daily at 8 AM and 8 PM
twiceDaily: '0 8,20 * * *'
};
// Use in repeatable jobs
await taskQueue.add('business-hours-check', { type: 'status' }, {
repeat: { cron: cronExamples.businessHours }
});Retrieve and manage existing repeatable jobs.
/**
* Get information about all repeatable jobs
* @param start - Start index (default: 0)
* @param end - End index (default: -1 for all)
* @param asc - Sort ascending by next execution time (default: false)
* @returns Promise that resolves to array of repeatable job information
*/
getRepeatableJobs(start?: number, end?: number, asc?: boolean): Promise<JobInformation[]>;
interface JobInformation {
/** Unique key for the repeatable job */
key: string;
/** Job name */
name: string;
/** Job ID if specified */
id?: string;
/** End date timestamp */
endDate?: number;
/** Timezone */
tz?: string;
/** Cron expression */
cron: string;
/** Interval in milliseconds */
every: number;
/** Next execution timestamp */
next: number;
}Usage Examples:
// Get all repeatable jobs
const repeatableJobs = await taskQueue.getRepeatableJobs();
console.log(`Found ${repeatableJobs.length} repeatable jobs`);
repeatableJobs.forEach(job => {
const nextRun = new Date(job.next);
console.log(`${job.name}: next run at ${nextRun.toISOString()}`);
if (job.cron) {
console.log(` Cron: ${job.cron}`);
}
if (job.every) {
console.log(` Interval: ${job.every}ms`);
}
});
// Get upcoming repeatable jobs (sorted by next execution)
const upcoming = await taskQueue.getRepeatableJobs(0, 10, true);
console.log('Next 10 jobs to run:');
upcoming.forEach(job => {
const nextRun = new Date(job.next);
const timeUntil = job.next - Date.now();
console.log(` ${job.name} in ${Math.round(timeUntil / 1000)}s`);
});Remove repeatable jobs by configuration or key.
/**
* Remove a repeatable job by repeat configuration
* @param repeat - Repeat options that match the original job
* @returns Promise that resolves when repeatable job is removed
*/
removeRepeatable(repeat: (CronRepeatOptions | EveryRepeatOptions) & { jobId?: string | number }): Promise<void>;
/**
* Remove a repeatable job by name and repeat configuration
* @param name - Job name
* @param repeat - Repeat options that match the original job
* @returns Promise that resolves when repeatable job is removed
*/
removeRepeatable(name: string, repeat: (CronRepeatOptions | EveryRepeatOptions) & { jobId?: string | number }): Promise<void>;
/**
* Remove a repeatable job by its unique key
* @param key - Repeatable job key from getRepeatableJobs()
* @returns Promise that resolves when repeatable job is removed
*/
removeRepeatableByKey(key: string): Promise<void>;Usage Examples:
// Remove repeatable job by matching original configuration
await taskQueue.removeRepeatable({
cron: '0 9 * * *',
tz: 'America/New_York'
});
// Remove named repeatable job
await taskQueue.removeRepeatable('daily-report', {
cron: '0 9 * * *',
tz: 'America/New_York'
});
// Remove by key (easier method)
const repeatableJobs = await taskQueue.getRepeatableJobs();
const reportJob = repeatableJobs.find(job => job.name === 'daily-report');
if (reportJob) {
await taskQueue.removeRepeatableByKey(reportJob.key);
console.log('Removed daily report job');
}
// Remove all repeatable jobs
const allRepeatable = await taskQueue.getRepeatableJobs();
for (const job of allRepeatable) {
await taskQueue.removeRepeatableByKey(job.key);
}
console.log('Removed all repeatable jobs');Get next instance and count repeatable jobs.
/**
* Get the next job instance for a repeatable job
* @param name - Job name
* @param data - Job data
* @param opts - Job options with repeat configuration
* @returns Promise that resolves to next job instance
*/
nextRepeatableJob(name: string, data: any, opts: JobOptions): Promise<Job>;
/**
* Get count of repeatable job definitions
* @returns Promise that resolves to number of repeatable jobs
*/
getRepeatableCount(): Promise<number>;Usage Examples:
// Get count of repeatable jobs
const repeatableCount = await taskQueue.getRepeatableCount();
console.log(`Total repeatable jobs defined: ${repeatableCount}`);
// Get next instance of a repeatable job (advanced usage)
const nextJob = await taskQueue.nextRepeatableJob('daily-report',
{ reportType: 'sales' },
{
repeat: { cron: '0 9 * * *' }
}
);
console.log(`Next job will run at: ${new Date(nextJob.timestamp)}`);Process repeatable jobs like regular jobs.
// Repeatable jobs are processed by regular processors
taskQueue.process('daily-report', async (job) => {
console.log(`Running daily report at ${new Date()}`);
console.log('Job data:', job.data);
// Generate report
const report = await generateDailyReport(job.data.reportType);
return {
reportGenerated: true,
reportId: report.id,
timestamp: Date.now()
};
});
taskQueue.process('health-check', async (job) => {
console.log(`Health check for ${job.data.service}`);
const status = await checkServiceHealth(job.data.service);
if (!status.healthy) {
// Alert on health issues
await sendAlert(`Service ${job.data.service} is unhealthy`);
}
return status;
});Work with timezones in repeatable jobs.
// Different timezone examples
const timezoneJobs = [
// New York time
{
name: 'ny-market-open',
data: { market: 'NYSE' },
repeat: {
cron: '30 9 * * 1-5', // 9:30 AM weekdays
tz: 'America/New_York'
}
},
// London time
{
name: 'london-report',
data: { region: 'Europe' },
repeat: {
cron: '0 17 * * 1-5', // 5 PM weekdays
tz: 'Europe/London'
}
},
// Tokyo time
{
name: 'tokyo-backup',
data: { system: 'trading' },
repeat: {
cron: '0 2 * * *', // 2 AM daily
tz: 'Asia/Tokyo'
}
},
// UTC (default)
{
name: 'global-sync',
data: { type: 'sync' },
repeat: {
cron: '0 0 * * *' // Midnight UTC
}
}
];
// Add all timezone-aware jobs
for (const jobDef of timezoneJobs) {
await taskQueue.add(jobDef.name, jobDef.data, { repeat: jobDef.repeat });
}Advanced scheduling patterns and use cases.
// Business day only job (excluding weekends and holidays)
await taskQueue.add('business-day-task', { type: 'report' }, {
repeat: {
cron: '0 9 * * 1-5', // Weekdays only
tz: 'America/New_York'
}
});
// Quarterly job (first Monday of quarter at 8 AM)
await taskQueue.add('quarterly-review', { quarter: 'Q1' }, {
repeat: {
cron: '0 8 1-7 1,4,7,10 1', // First Monday of Jan, Apr, Jul, Oct
tz: 'UTC'
}
});
// End-of-month job (last day of month at 11 PM)
await taskQueue.add('month-end-close', { type: 'accounting' }, {
repeat: {
cron: '0 23 28-31 * *', // Run on days 28-31 of month
tz: 'America/New_York'
}
});
// High-frequency job with limit (every 30 seconds for 1 hour)
await taskQueue.add('burst-monitoring', { duration: '1hour' }, {
repeat: {
every: 30000, // Every 30 seconds
limit: 120, // Stop after 2 hours (120 * 30sec = 1 hour)
endDate: new Date(Date.now() + 60 * 60 * 1000) // Also set end date
}
});
// Seasonal job (summer months only)
await taskQueue.add('summer-maintenance', { season: 'summer' }, {
repeat: {
cron: '0 2 1 6-8 *', // 1st of June, July, August at 2 AM
tz: 'UTC'
}
});Monitor repeatable job execution and health.
// Monitor repeatable job execution
taskQueue.on('completed', (job, result) => {
if (job.opts.repeat) {
console.log(`Repeatable job ${job.name} completed:`, result);
// Log execution time for performance monitoring
const executionTime = job.finishedOn - job.processedOn;
console.log(`Execution time: ${executionTime}ms`);
}
});
taskQueue.on('failed', (job, err) => {
if (job.opts.repeat) {
console.error(`Repeatable job ${job.name} failed:`, err.message);
// Alert on repeatable job failures
sendAlert(`Repeatable job ${job.name} failed: ${err.message}`);
}
});
// Periodic health check for repeatable jobs
async function checkRepeatableJobHealth() {
const repeatableJobs = await taskQueue.getRepeatableJobs();
const now = Date.now();
for (const job of repeatableJobs) {
const timeSinceNext = now - job.next;
// Alert if job is overdue by more than 5 minutes
if (timeSinceNext > 5 * 60 * 1000) {
console.warn(`Repeatable job ${job.name} is overdue by ${Math.round(timeSinceNext / 1000)}s`);
}
}
}
// Run health check every 10 minutes
setInterval(checkRepeatableJobHealth, 10 * 60 * 1000);