super-simple-scheduler
v1.4.6
Published
A simple scheduler for Node.js
Maintainers
Readme
Super Simple Scheduler
A lightweight and easy-to-use job scheduler for Node.js with support for repeated jobs, retries, and multiple storage backends.
GitHub: https://github.com/ajhollid/super-simple-scheduler
Features
- Schedule jobs with optional repeat intervals
- Automatic retry on failure with configurable max retries
- Simple API to add job templates and jobs
- Written in TypeScript with type definitions
- Job pausing, resuming, and removal
- Comprehensive logging with Winston
- Fast in-memory storage backend
- Async/await API throughout
- ES Module support
Installation
npm install super-simple-schedulerTypeScript Support
The package includes full TypeScript support with type definitions. You can import the SchedulerOptions type for better intellisense:
import { Scheduler, SchedulerOptions } from "super-simple-scheduler";
const options: SchedulerOptions = {
logLevel: "info",
dev: true,
processEvery: 1000,
};
const scheduler = new Scheduler(options);Quick Start
import { Scheduler, SchedulerOptions } from "super-simple-scheduler";
// Create a scheduler instance
const scheduler = new Scheduler({
logLevel: "info",
dev: false,
processEvery: 1000, // Process jobs every 1 second
});
// Add a job template
await scheduler.addTemplate("sendEmail", async (data) => {
// Your job logic here
console.log("Sending email to:", data.recipient);
});
// Add a job
await scheduler.addJob({
template: "sendEmail",
repeat: 60000, // Run every minute
data: { recipient: "[email protected]" },
});
// Start the scheduler
await scheduler.start();API Reference
Scheduler Class
The main scheduler class that manages job execution.
Constructor
new Scheduler(options: SchedulerOptions)Parameters:
options.logLevel(optional): Logging level ('none', 'debug', 'info', 'warn', 'error'). Default: 'info'options.dev(optional): Development mode flag. Default: falseoptions.processEvery(optional): Interval in milliseconds to process jobs. Default: 1000
Example:
const scheduler = new Scheduler({
logLevel: "debug",
dev: true,
processEvery: 5000, // Process every 5 seconds
});Methods
start(): Promise<boolean>
Starts the scheduler and begins processing jobs at the configured interval.
Returns: Promise<boolean> - true if started successfully
Example:
const success = await scheduler.start();
if (success) {
console.log("Scheduler started");
}stop(): Promise<boolean>
Stops the scheduler and clears the processing interval.
Returns: Promise<boolean> - true if stopped successfully
Example:
const success = scheduler.stop();
if (success) {
console.log("Scheduler stopped");
}addTemplate(name: string, template: Function): Promise<boolean>
Registers a job template function that can be referenced by jobs.
Parameters:
name: Unique identifier for the templatetemplate: Function to execute when the job runs. Can be async or sync.
Returns: Promise<boolean> - true if template was added successfully
Example:
await scheduler.addTemplate("processData", async (data) => {
// Process the data
await processUserData(data);
});
await scheduler.addTemplate("sendNotification", (data) => {
// Send notification
sendPushNotification(data.userId, data.message);
});addJob(options): Promise<boolean>
Adds a new job to the scheduler.
Parameters:
options.id(optional): Unique identifier for the job. If not provided, a UUID will be generatedoptions.template: Name of the template to use for this joboptions.repeat(optional): Interval in milliseconds between job executions. If null, job runs onceoptions.data(optional): Data to pass to the job template functionoptions.active(optional): Whether the job should be active. Default: trueoptions.startAt(optional): Timestamp when the job should start running. If not provided, job runs immediately
Returns: Promise<boolean> - true if job was added or updated successfully
Note: If a job with the same ID already exists, it will be updated with the new properties instead of failing.
Example:
// One-time job
await scheduler.addJob({
template: "sendWelcomeEmail",
data: { userId: "123", email: "[email protected]" },
});
// Repeating job
await scheduler.addJob({
id: "daily-cleanup",
template: "cleanupDatabase",
repeat: 24 * 60 * 60 * 1000, // 24 hours
data: { tables: ["logs", "temp_data"] },
});
// Job with delayed start
await scheduler.addJob({
template: "sendReminder",
startAt: Date.now() + 60000, // Start in 1 minute
repeat: 60 * 60 * 1000, // Then every hour
data: { message: "Don't forget!" },
});
// Inactive job (will be paused)
await scheduler.addJob({
template: "maintenance",
repeat: 60 * 60 * 1000, // 1 hour
active: false,
});pauseJob(id: string | number): Promise<boolean>
Pauses a job, preventing it from executing.
Parameters:
id: Job identifier
Returns: Promise<boolean> - true if job was paused successfully, false if job not found
Example:
const success = await scheduler.pauseJob("daily-cleanup");
if (success) {
console.log("Job paused");
}resumeJob(id: string | number): Promise<boolean>
Resumes a paused job, allowing it to execute again.
Parameters:
id: Job identifier
Returns: Promise<boolean> - true if job was resumed successfully, false if job not found
Example:
const success = await scheduler.resumeJob("daily-cleanup");
if (success) {
console.log("Job resumed");
}removeJob(id: string | number): Promise<boolean>
Removes a job from the scheduler.
Parameters:
id: Job identifier
Returns: Promise<boolean> - true if job was removed successfully, false if job not found
Example:
const success = await scheduler.removeJob("daily-cleanup");
if (success) {
console.log("Job removed");
}getJob(id: string | number): Promise<IJob | null>
Returns a specific job by ID.
Parameters:
id: Job identifier
Returns: Promise<IJob | null> - Job object or null if not found
Example:
const job = await scheduler.getJob("daily-cleanup");
if (job) {
console.log(
`Job ${job.id}: ${job.template} (${job.active ? "active" : "paused"})`
);
}getJobs(): Promise<IJob[]>
Returns an array of all jobs in the scheduler.
Returns: Promise<IJob[]> - Array of job objects
Example:
const jobs = await scheduler.getJobs();
console.log(`Scheduler has ${jobs.length} jobs`);
jobs.forEach((job) => {
console.log(
`Job ${job.id}: ${job.template} (${job.active ? "active" : "paused"})`
);
});flushJobs(): Promise<boolean>
Removes all jobs from the scheduler.
Returns: Promise<boolean> - true if jobs were flushed successfully
Example:
const success = await scheduler.flushJobs();
if (success) {
console.log("All jobs removed");
}updateJob(id: string | number, updates: Partial<IJob>): Promise<boolean>
Updates a job with new properties.
Parameters:
id: Job identifierupdates: Partial job object with properties to update
Returns: Promise<boolean> - true if job was updated successfully, false if job not found
Example:
const success = await scheduler.updateJob("daily-cleanup", {
repeat: 12 * 60 * 60 * 1000, // 12 hours
data: { newData: "updated" },
});
if (success) {
console.log("Job updated");
}Job Interface
Jobs have the following structure:
interface IJob {
id: string | number; // Unique identifier
template: string; // Template name
data?: any; // Data passed to template function
repeat?: number; // Interval in milliseconds (null for one-time)
maxRetries?: number; // Maximum retry attempts (default: 3)
active: boolean; // Whether job is active
startAt?: number | null; // Timestamp when job should start running
lastRunAt?: number | null; // Timestamp of last execution
lastFinishedAt?: number | null; // Timestamp of last completion
lockedAt?: number | null; // Timestamp when job is being processed
lastFailedAt?: number | null; // Timestamp of last failure
lastFailReason?: string | null; // Reason for last failure
failCount?: number; // Number of failures
runCount?: number; // Number of successful runs
}Storage
The scheduler uses a fast in-memory storage backend that's perfect for most use cases:
In-Memory Store
const scheduler = new Scheduler({
logLevel: "info",
});Pros:
- Fast and lightweight
- Minimal external dependencies
- Perfect for development and testing
- Simple setup and configuration
Cons:
- Jobs are lost on process restart
- Not suitable for production with multiple instances
Note: For production applications that need persistence or multiple instances, consider implementing your own storage backend or using a job queue system like Bull or Agenda.
Job Execution Logic
The scheduler uses a sophisticated job execution system that determines when jobs should run based on multiple factors:
Job Execution Conditions
A job will run when all of the following conditions are met:
- Active Status: Job is active (
active: true) - Start Time: If
startAtis set, the current time must be >=startAt - Execution History:
- If job has never run before (
lastRunAt: null), it runs immediately - If job is one-time (
repeat: null) and has already run, it doesn't run again - If job is repeating, enough time must have passed since the last run
- If job has never run before (
Timing Examples
// Immediate execution (default)
await scheduler.addJob({
template: "sendEmail",
data: { to: "[email protected]" },
});
// Runs on next processing cycle (within 1 second)
// Delayed start
await scheduler.addJob({
template: "sendEmail",
startAt: Date.now() + 60000, // Start in 1 minute
data: { to: "[email protected]" },
});
// Runs 1 minute from now
// One-time job
await scheduler.addJob({
template: "sendEmail",
// No repeat = one-time job
data: { to: "[email protected]" },
});
// Runs once, then never again
// Repeating job
await scheduler.addJob({
template: "sendEmail",
repeat: 60000, // Every minute
data: { to: "[email protected]" },
});
// Runs every minute
// Repeating job with delayed start
await scheduler.addJob({
template: "sendEmail",
startAt: Date.now() + 60000, // Start in 1 minute
repeat: 60000, // Then every minute
data: { to: "[email protected]" },
});
// Starts in 1 minute, then runs every minuteConfiguration
Processing Interval
The scheduler processes jobs at regular intervals. The default interval is 1 second, but you can modify it:
const scheduler = new Scheduler({
processEvery: 5000, // Process every 5 seconds
});Retry Behavior
Jobs automatically retry on failure with a configurable maximum number of attempts:
await scheduler.addJob({
template: "unreliableTask",
repeat: 60000,
maxRetries: 5, // Will retry up to 5 times on failure
data: { task: "important" },
});Error Handling
The scheduler includes comprehensive error handling:
- Failed jobs are automatically retried up to the configured
maxRetries - All errors are logged with detailed information using Winston
- Jobs that fail all retry attempts are logged as errors
- One-time jobs (
repeat: null) are removed after execution regardless of success/failure
Logging
The scheduler uses Winston for logging with configurable levels:
const scheduler = new Scheduler({
logLevel: "debug", // 'none', 'debug', 'info', 'warn', 'error'
dev: true, // Enhanced logging for development
});Examples
Email Scheduler
import { Scheduler } from "super-simple-scheduler";
const scheduler = new Scheduler({
logLevel: "info",
});
// Email template
await scheduler.addTemplate("sendEmail", async (data) => {
await sendEmail(data.to, data.subject, data.body);
});
// Daily digest
await scheduler.addJob({
id: "daily-digest",
template: "sendEmail",
repeat: 24 * 60 * 60 * 1000,
data: {
to: "[email protected]",
subject: "Daily Digest",
body: "Here is your daily summary...",
},
});
await scheduler.start();Data Processing Pipeline
const scheduler = new Scheduler({
logLevel: "debug",
});
// Data processing templates
await scheduler.addTemplate("fetchData", async (data) => {
const rawData = await fetchFromAPI(data.endpoint);
await saveToDatabase(rawData);
});
await scheduler.addTemplate("processData", async (data) => {
const rawData = await getFromDatabase();
const processed = await transformData(rawData);
await saveProcessedData(processed);
});
// Schedule jobs
await scheduler.addJob({
id: "fetch-hourly",
template: "fetchData",
repeat: 60 * 60 * 1000,
data: { endpoint: "/api/data" },
});
await scheduler.addJob({
id: "process-daily",
template: "processData",
repeat: 24 * 60 * 60 * 1000,
});
await scheduler.start();Job Management Example
const scheduler = new Scheduler({});
// Add a job
await scheduler.addJob({
id: "test-job",
template: "testTemplate",
repeat: 5000,
data: { message: "Hello World" },
});
// Add a job with delayed start
await scheduler.addJob({
id: "delayed-job",
template: "testTemplate",
startAt: Date.now() + 10000, // Start in 10 seconds
repeat: 5000,
data: { message: "Delayed Hello World" },
});
// Update the same job (will update existing job instead of failing)
await scheduler.addJob({
id: "test-job",
template: "testTemplate",
repeat: 10000, // Changed from 5000 to 10000
data: { message: "Updated message" },
});
// Pause the job
await scheduler.pauseJob("test-job");
// Check job status
const job = await scheduler.getJob("test-job");
console.log(job?.active); // false
// Resume the job
await scheduler.resumeJob("test-job");
// Get all jobs
const allJobs = await scheduler.getJobs();
console.log(`Total jobs: ${allJobs.length}`);
// Update job
await scheduler.updateJob("test-job", {
repeat: 10000, // Change to 10 seconds
data: { message: "Updated message" },
});
// Remove job
await scheduler.removeJob("test-job");Development
Building
npm run buildTesting
# Run all tests
npm test
# Run tests with coverage
npm run test-coverageDevelopment Mode
npm run devLicense
MIT
