Cron Package
The @hazeljs/cron package provides scheduled task capabilities for your HazelJS applications. It allows you to run functions at specified intervals using cron expressions, with support for job management, error handling, and execution tracking.
Purpose
Many applications need to run periodic tasks like data cleanup, report generation, cache warming, or sending notifications. Implementing scheduled tasks requires managing cron expressions, handling errors, tracking execution, and ensuring tasks don't overlap. The @hazeljs/cron package simplifies this by providing:
- Cron Expressions: Support for standard cron syntax for flexible scheduling
- Decorator-Based: Use
@Crondecorator to mark methods as scheduled tasks - Job Management: Start, stop, enable, and disable jobs programmatically
- Error Handling: Built-in error handling with callbacks
- Execution Tracking: Monitor job execution, run counts, and timing
Architecture
The package uses a service-based approach with job registration and lifecycle management:
Key Components
- CronService: Manages all scheduled jobs
- CronJob: Individual job instance with execution logic
- @Cron Decorator: Declarative way to create scheduled tasks
- Job Options: Configuration for execution behavior
Advantages
1. Simple API
Use decorators to create scheduled tasks—no need to manually manage timers or intervals.
2. Flexible Scheduling
Support for standard cron expressions allows precise control over when tasks run.
3. Job Management
Programmatically control jobs—start, stop, enable, or disable them as needed.
4. Error Resilience
Built-in error handling ensures one failing job doesn't crash your application.
5. Monitoring
Track job execution, run counts, and timing for better observability.
6. Production Features
Support for max runs, run-on-init, and execution callbacks for complex scenarios.
Installation
npm install @hazeljs/cron
Quick Start
Basic Setup
import { HazelModule } from '@hazeljs/core';
import { CronModule } from '@hazeljs/cron';
@HazelModule({
imports: [CronModule],
})
export class AppModule {}
Cron Service
Registering Jobs
Use the CronService to register and manage scheduled jobs:
import { Injectable, OnModuleInit } from '@hazeljs/core';
import { CronService } from '@hazeljs/cron';
@Injectable()
export class ScheduledTasksService implements OnModuleInit {
constructor(private readonly cronService: CronService) {}
onModuleInit() {
// Run every minute
this.cronService.registerJob(
'cleanup-temp-files',
'0 * * * * *', // Every minute
async () => {
await this.cleanupTempFiles();
},
{
enabled: true,
runOnInit: false,
}
);
// Run every hour
this.cronService.registerJob(
'send-reports',
'0 0 * * * *', // Every hour
async () => {
await this.sendHourlyReports();
}
);
}
private async cleanupTempFiles() {
// Cleanup logic
}
private async sendHourlyReports() {
// Report sending logic
}
}
@Cron Decorator
The @Cron decorator is a method decorator that marks methods as scheduled tasks. It automatically registers them with the CronService when the module initializes.
Understanding @Cron Decorator
Purpose: Declaratively define scheduled tasks using cron expressions. The framework automatically manages job lifecycle, execution, and error handling.
How it works:
- Metadata Storage: Stores cron configuration in method metadata
- Module Initialization: When module loads,
CronServicescans for@Crondecorators - Job Registration: Each decorated method is registered as a cron job
- Automatic Scheduling: Jobs are scheduled based on cron expressions
- Execution Management: Framework handles timing, execution, and error recovery
Configuration Options:
interface CronOptions {
cronTime: string; // Cron expression (required)
name?: string; // Job name (default: ClassName.methodName)
enabled?: boolean; // Enable/disable job (default: true)
runOnInit?: boolean; // Run immediately on startup (default: false)
maxRuns?: number; // Maximum number of executions (optional)
onComplete?: () => void; // Callback after successful execution
onError?: (error: Error) => void; // Error handler callback
}
Example with Detailed Explanation:
import { Injectable } from '@hazeljs/core';
import { Cron } from '@hazeljs/cron';
@Injectable()
export class TaskService {
// @Cron is a method decorator that marks this method as a scheduled task
@Cron({
cronTime: '0 0 * * * *', // Cron expression: every hour at minute 0
// Format: second minute hour day month weekday
// '0 0 * * * *' = at 0 seconds, 0 minutes, every hour
name: 'hourly-task', // Custom name for this job
enabled: true, // Job is active (can be disabled programmatically)
runOnInit: false, // Don't run immediately on startup
// Set to true if you want the task to run when the app starts
})
async hourlyTask() {
// This method will be called every hour
// Framework handles:
// - Scheduling the execution
// - Error handling
// - Preventing overlapping executions
// - Logging execution status
console.log('Running hourly task');
}
@Cron({
cronTime: '0 0 0 * * *', // Every day at midnight (00:00:00)
name: 'daily-backup',
runOnInit: true, // Run immediately when app starts
// Useful for ensuring backup runs even if app restarts
})
async dailyBackup() {
// Runs once per day at midnight
// Also runs immediately when application starts (runOnInit: true)
console.log('Running daily backup');
}
@Cron({
cronTime: '0 */5 * * * *', // Every 5 minutes
// */5 means "every 5 units"
name: 'health-check',
maxRuns: 10, // Stop after 10 executions
// Useful for one-time tasks or limited-run jobs
onError: (error) => {
// Custom error handling
console.error('Health check failed:', error);
// Could send alert, log to monitoring service, etc.
},
})
async healthCheck() {
// Runs every 5 minutes, but stops after 10 runs
// After 10 executions, the job is automatically stopped
console.log('Running health check');
}
@Cron({
cronTime: '0 0 9 * * 1', // Every Monday at 9 AM
// 1 = Monday (0 = Sunday, 1 = Monday, ..., 6 = Saturday)
name: 'weekly-report',
onComplete: () => {
// Callback after successful execution
console.log('Weekly report generated successfully');
},
})
async generateWeeklyReport() {
// Runs every Monday at 9:00 AM
// onComplete callback executes after method finishes successfully
console.log('Generating weekly report');
}
}
Cron Expression Format:
The cron expression uses 6 fields: second minute hour day month weekday
┌───────────── second (0 - 59)
│ ┌─────────── minute (0 - 59)
│ │ ┌───────── hour (0 - 23)
│ │ │ ┌─────── day of month (1 - 31)
│ │ │ │ ┌───── month (1 - 12)
│ │ │ │ │ ┌─── day of week (0 - 7, 0 or 7 is Sunday)
│ │ │ │ │ │
* * * * * *
Special Characters:
*- Any value (every second, every minute, etc.),- Value list separator (e.g.,1,3,5= 1, 3, or 5)-- Range (e.g.,1-5= 1 through 5)/- Step values (e.g.,*/5= every 5 units)?- No specific value (used in day fields)
Common Patterns Explained:
// Every second
'* * * * * *'
// Every field is *, so it runs every second
// Every 30 seconds
'*/30 * * * * *'
// */30 in seconds field = every 30 seconds
// Every minute (at :00 seconds)
'0 * * * * *'
// 0 in seconds = at second 0, * in minutes = every minute
// Every 5 minutes
'0 */5 * * * *'
// At second 0, every 5 minutes
// Every hour (at :00:00)
'0 0 * * * *'
// At second 0, minute 0, every hour
// Every day at midnight
'0 0 0 * * *'
// At 00:00:00 every day
// Every Monday at 9 AM
'0 0 9 * * 1'
// At 09:00:00, every Monday
// First day of month at midnight
'0 0 0 1 * *'
// At 00:00:00 on the 1st of every month
Job Lifecycle:
- Registration: Job registered when module initializes
- Scheduling: Framework calculates next execution time from cron expression
- Execution: Method called at scheduled time
- Completion:
onCompletecallback fired if provided - Rescheduling: Next execution time calculated for subsequent runs
- Stopping: Job stops if
maxRunsreached or manually stopped
Error Handling:
@Cron({
cronTime: '0 * * * * *',
name: 'error-prone-task',
onError: (error: Error) => {
// Custom error handling
// This prevents the error from crashing your application
console.error('Task failed:', error);
// You can:
// - Send alerts
// - Log to monitoring service
// - Retry logic
// - Notify administrators
},
})
async errorProneTask() {
// If this throws an error, onError callback is called
// The job continues to run on schedule despite errors
throw new Error('Something went wrong');
}
Best Practices:
- Use descriptive names: Makes debugging and monitoring easier
- Handle errors: Always provide
onErrorcallback for critical tasks - Avoid overlapping executions: Framework prevents this, but be aware of long-running tasks
- Test cron expressions: Use online cron validators before deploying
- Monitor execution: Track job execution times and frequencies
- Use runOnInit carefully: Only for tasks safe to run immediately on startup
Cron Expression Format
Cron expressions use the format: second minute hour day month weekday
* * * * * *
│ │ │ │ │ │
│ │ │ │ │ └─── Day of week (0-7, 0 or 7 is Sunday)
│ │ │ │ └───── Month (1-12)
│ │ │ └─────── Day of month (1-31)
│ │ └───────── Hour (0-23)
│ └─────────── Minute (0-59)
└───────────── Second (0-59)
Common Patterns
// Every second
'* * * * * *'
// Every 5 seconds
'*/5 * * * * *'
// Every minute
'0 * * * * *'
// Every 5 minutes
'0 */5 * * * *'
// Every hour
'0 0 * * * *'
// Every day at midnight
'0 0 0 * * *'
// Every Monday at 9 AM
'0 0 9 * * 1'
// Every first day of month at midnight
'0 0 0 1 * *'
Job Options
interface CronOptions {
cronTime: string; // Cron expression
name?: string; // Job name
enabled?: boolean; // Enable/disable job
runOnInit?: boolean; // Run immediately on registration
maxRuns?: number; // Maximum number of runs
onComplete?: () => void; // Callback on completion
onError?: (error: Error) => void; // Error handler
}
Job Management
Start/Stop Jobs
@Injectable()
export class JobManagerService {
constructor(private readonly cronService: CronService) {}
startJob(name: string) {
this.cronService.startJob(name);
}
stopJob(name: string) {
this.cronService.stopJob(name);
}
enableJob(name: string) {
this.cronService.enableJob(name);
}
disableJob(name: string) {
this.cronService.disableJob(name);
}
}
Get Job Status
// Get status of a specific job
const status = this.cronService.getJobStatus('hourly-task');
console.log({
name: status.name,
isRunning: status.isRunning,
lastExecution: status.lastExecution,
nextExecution: status.nextExecution,
runCount: status.runCount,
enabled: status.enabled,
});
// Get status of all jobs
const allStatuses = this.cronService.getAllJobStatuses();
Job Control
// Stop all jobs
this.cronService.stopAll();
// Start all jobs
this.cronService.startAll();
// Clear all jobs
this.cronService.clearAll();
// Get job count
const count = this.cronService.getJobCount();
Error Handling
Handle errors in cron jobs:
@Cron({
cronTime: '0 * * * * *',
name: 'error-prone-task',
onError: (error: Error) => {
console.error('Cron job error:', error);
// Send alert, log to monitoring service, etc.
},
})
async errorProneTask() {
// This might throw an error
throw new Error('Something went wrong');
}
Complete Example
import { Injectable, OnModuleInit } from '@hazeljs/core';
import { CronService, Cron } from '@hazeljs/cron';
@Injectable()
export class ScheduledTasksService implements OnModuleInit {
constructor(private readonly cronService: CronService) {}
onModuleInit() {
// Register jobs programmatically
this.cronService.registerJob(
'cleanup',
'0 0 2 * * *', // Daily at 2 AM
async () => {
await this.cleanupOldData();
},
{
enabled: true,
onError: (error) => {
console.error('Cleanup job failed:', error);
},
}
);
}
// Use decorator for scheduled tasks
@Cron({
cronTime: '0 */10 * * * *', // Every 10 minutes
name: 'sync-data',
runOnInit: true, // Run immediately on startup
})
async syncData() {
console.log('Syncing data...');
// Sync logic
}
@Cron({
cronTime: '0 0 * * * *', // Every hour
name: 'send-notifications',
maxRuns: 24, // Stop after 24 runs (24 hours)
})
async sendNotifications() {
console.log('Sending notifications...');
// Notification logic
}
private async cleanupOldData() {
// Cleanup logic
}
}
Best Practices
-
Use descriptive names: Give your cron jobs meaningful names for easier debugging.
-
Handle errors: Always implement error handlers to prevent job failures from crashing your application.
-
Monitor execution: Track job execution times and frequencies.
-
Limit runs: Use
maxRunsfor jobs that should only run a certain number of times. -
Test locally: Test cron expressions before deploying to production.
-
Use runOnInit carefully: Only enable
runOnInitfor jobs that are safe to run immediately on startup.