1 min read
KI-EntwicklungCron Jobs & Task Scheduling in Node.js
Task Scheduling für Node.js. Cron Syntax, node-cron, BullMQ Repeatable Jobs und Serverless Scheduling.
Cron JobsTask SchedulingNode.jsnode-cronBullMQScheduled Tasks

Cron Jobs & Task Scheduling in Node.js
Meta-Description: Task Scheduling für Node.js. Cron Syntax, node-cron, BullMQ Repeatable Jobs und Serverless Scheduling.
Keywords: Cron Jobs, Task Scheduling, Node.js, node-cron, BullMQ, Scheduled Tasks, Background Jobs
Einführung
Automatisierte Aufgaben sind das Rückgrat jeder Anwendung: Backups, Reports, Cleanup, Sync-Jobs. Mit Cron Jobs laufen sie zuverlässig zu definierten Zeiten – ohne manuellen Eingriff.
Cron Syntax
┌─────────────────────────────────────────────────────────────┐
│ CRON EXPRESSION │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────── Minute (0-59) │
│ │ ┌───────────── Stunde (0-23) │
│ │ │ ┌───────────── Tag des Monats (1-31) │
│ │ │ │ ┌───────────── Monat (1-12) │
│ │ │ │ │ ┌───────────── Wochentag (0-7, 0 und 7 = Sonntag)│
│ │ │ │ │ │ │
│ * * * * * │
│ │
│ Beispiele: │
│ ├── * * * * * → Jede Minute │
│ ├── 0 * * * * → Jede volle Stunde │
│ ├── 0 0 * * * → Täglich um Mitternacht │
│ ├── 0 8 * * 1-5 → Mo-Fr um 8:00 │
│ ├── 0 0 1 * * → Am 1. jeden Monats │
│ ├── */15 * * * * → Alle 15 Minuten │
│ └── 0 9,18 * * * → Um 9:00 und 18:00 │
│ │
└─────────────────────────────────────────────────────────────┘node-cron Setup
npm install node-cron
npm install -D @types/node-cronimport cron from 'node-cron';
// Basic Scheduled Task
cron.schedule('* * * * *', () => {
console.log('Runs every minute');
});
// Mit Timezone
cron.schedule('0 8 * * *', () => {
console.log('Daily at 8:00 AM Berlin time');
}, {
timezone: 'Europe/Berlin'
});
// Task mit Start/Stop
const task = cron.schedule('*/5 * * * *', () => {
console.log('Every 5 minutes');
}, {
scheduled: false // Nicht automatisch starten
});
// Manuell starten/stoppen
task.start();
// task.stop();
// Validation
const isValid = cron.validate('* * * * *'); // truePraktische Scheduler-Klasse
import cron, { ScheduledTask } from 'node-cron';
interface Job {
name: string;
schedule: string;
handler: () => Promise<void>;
enabled: boolean;
timezone?: string;
}
class TaskScheduler {
private tasks: Map<string, ScheduledTask> = new Map();
private jobs: Map<string, Job> = new Map();
register(job: Job) {
this.jobs.set(job.name, job);
if (job.enabled) {
this.startJob(job.name);
}
}
startJob(name: string) {
const job = this.jobs.get(name);
if (!job) throw new Error(`Job ${name} not found`);
if (this.tasks.has(name)) {
console.log(`Job ${name} already running`);
return;
}
const task = cron.schedule(job.schedule, async () => {
const startTime = Date.now();
console.log(`[${name}] Starting...`);
try {
await job.handler();
console.log(`[${name}] Completed in ${Date.now() - startTime}ms`);
} catch (error) {
console.error(`[${name}] Failed:`, error);
// Alert senden
}
}, {
timezone: job.timezone || 'Europe/Berlin'
});
this.tasks.set(name, task);
console.log(`[${name}] Scheduled: ${job.schedule}`);
}
stopJob(name: string) {
const task = this.tasks.get(name);
if (task) {
task.stop();
this.tasks.delete(name);
console.log(`[${name}] Stopped`);
}
}
stopAll() {
for (const [name, task] of this.tasks) {
task.stop();
console.log(`[${name}] Stopped`);
}
this.tasks.clear();
}
getStatus(): Array<{ name: string; schedule: string; running: boolean }> {
return Array.from(this.jobs.values()).map(job => ({
name: job.name,
schedule: job.schedule,
running: this.tasks.has(job.name)
}));
}
}
// Verwendung
const scheduler = new TaskScheduler();
scheduler.register({
name: 'daily-backup',
schedule: '0 2 * * *',
handler: async () => {
await performDatabaseBackup();
},
enabled: true
});
scheduler.register({
name: 'hourly-sync',
schedule: '0 * * * *',
handler: async () => {
await syncExternalData();
},
enabled: true
});
scheduler.register({
name: 'weekly-report',
schedule: '0 9 * * 1', // Montag 9:00
handler: async () => {
await generateWeeklyReport();
await sendReportEmail();
},
enabled: true
});
// Graceful Shutdown
process.on('SIGTERM', () => {
scheduler.stopAll();
process.exit(0);
});BullMQ Repeatable Jobs
import { Queue, Worker } from 'bullmq';
import IORedis from 'ioredis';
const connection = new IORedis(process.env.REDIS_URL!);
// Queue für Scheduled Jobs
const scheduledQueue = new Queue('scheduled-tasks', { connection });
// Repeatable Jobs registrieren
async function setupScheduledJobs() {
// Täglich um 2:00 UTC
await scheduledQueue.add('database-backup', {}, {
repeat: {
pattern: '0 2 * * *',
tz: 'Europe/Berlin'
},
jobId: 'daily-backup' // Verhindert Duplikate
});
// Alle 5 Minuten
await scheduledQueue.add('health-check', {}, {
repeat: {
every: 5 * 60 * 1000 // Millisekunden
},
jobId: 'health-check'
});
// Wöchentlich Montag 9:00
await scheduledQueue.add('weekly-report', { type: 'weekly' }, {
repeat: {
pattern: '0 9 * * 1',
tz: 'Europe/Berlin'
},
jobId: 'weekly-report'
});
// Monatlich am 1.
await scheduledQueue.add('monthly-cleanup', {}, {
repeat: {
pattern: '0 3 1 * *',
tz: 'Europe/Berlin'
},
jobId: 'monthly-cleanup'
});
}
// Worker für Scheduled Jobs
const worker = new Worker('scheduled-tasks', async (job) => {
console.log(`Processing ${job.name}`);
switch (job.name) {
case 'database-backup':
await performDatabaseBackup();
break;
case 'health-check':
await runHealthChecks();
break;
case 'weekly-report':
await generateAndSendReport('weekly');
break;
case 'monthly-cleanup':
await cleanupOldData();
break;
default:
console.warn(`Unknown job: ${job.name}`);
}
return { completed: true };
}, { connection });
worker.on('completed', (job, result) => {
console.log(`Job ${job.name} completed`);
});
worker.on('failed', (job, error) => {
console.error(`Job ${job?.name} failed:`, error);
// Alert senden
});
// Repeatable Jobs verwalten
async function listScheduledJobs() {
const jobs = await scheduledQueue.getRepeatableJobs();
return jobs.map(job => ({
name: job.name,
pattern: job.pattern || `every ${job.every}ms`,
next: new Date(job.next).toISOString()
}));
}
async function removeScheduledJob(jobId: string) {
const jobs = await scheduledQueue.getRepeatableJobs();
const job = jobs.find(j => j.id === jobId);
if (job) {
await scheduledQueue.removeRepeatableByKey(job.key);
console.log(`Removed job: ${jobId}`);
}
}Serverless Cron (Vercel/AWS)
// Vercel Cron Jobs (vercel.json)
{
"crons": [
{
"path": "/api/cron/daily-backup",
"schedule": "0 2 * * *"
},
{
"path": "/api/cron/hourly-sync",
"schedule": "0 * * * *"
}
]
}
// app/api/cron/daily-backup/route.ts
import { NextRequest, NextResponse } from 'next/server';
export const runtime = 'edge';
export const maxDuration = 60; // 60 Sekunden max
export async function GET(request: NextRequest) {
// Verify Cron Secret (Security)
const authHeader = request.headers.get('authorization');
if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
try {
await performBackup();
return NextResponse.json({
success: true,
timestamp: new Date().toISOString()
});
} catch (error) {
console.error('Backup failed:', error);
return NextResponse.json({
success: false,
error: (error as Error).message
}, { status: 500 });
}
}
// AWS EventBridge + Lambda
// serverless.yml
// functions:
// dailyBackup:
// handler: src/handlers/backup.handler
// events:
// - schedule: cron(0 2 * * ? *)Job Locking (Prevent Duplicates)
import { Redis } from 'ioredis';
const redis = new Redis(process.env.REDIS_URL!);
async function withJobLock<T>(
jobName: string,
ttlSeconds: number,
handler: () => Promise<T>
): Promise<T | null> {
const lockKey = `job-lock:${jobName}`;
// Versuche Lock zu erwerben
const acquired = await redis.set(lockKey, Date.now().toString(), 'EX', ttlSeconds, 'NX');
if (!acquired) {
console.log(`Job ${jobName} already running, skipping`);
return null;
}
try {
return await handler();
} finally {
// Lock freigeben
await redis.del(lockKey);
}
}
// Verwendung
cron.schedule('*/5 * * * *', async () => {
await withJobLock('sync-products', 300, async () => {
// Dieser Code läuft garantiert nur einmal
await syncProducts();
});
});Monitoring & Alerting
interface JobExecution {
jobName: string;
startedAt: Date;
completedAt?: Date;
status: 'running' | 'completed' | 'failed';
error?: string;
duration?: number;
}
class JobMonitor {
private executions: Map<string, JobExecution> = new Map();
async trackExecution<T>(
jobName: string,
handler: () => Promise<T>
): Promise<T> {
const executionId = `${jobName}-${Date.now()}`;
this.executions.set(executionId, {
jobName,
startedAt: new Date(),
status: 'running'
});
try {
const result = await handler();
const execution = this.executions.get(executionId)!;
execution.completedAt = new Date();
execution.status = 'completed';
execution.duration = execution.completedAt.getTime() - execution.startedAt.getTime();
// Log to database
await this.saveExecution(execution);
return result;
} catch (error) {
const execution = this.executions.get(executionId)!;
execution.completedAt = new Date();
execution.status = 'failed';
execution.error = (error as Error).message;
execution.duration = execution.completedAt.getTime() - execution.startedAt.getTime();
await this.saveExecution(execution);
await this.sendAlert(execution);
throw error;
} finally {
// Cleanup alte Executions
setTimeout(() => this.executions.delete(executionId), 60000);
}
}
private async saveExecution(execution: JobExecution) {
await db.jobExecution.create({ data: execution });
}
private async sendAlert(execution: JobExecution) {
// Slack, Email, PagerDuty, etc.
await slack.send({
text: `❌ Job Failed: ${execution.jobName}`,
attachments: [{
color: 'danger',
fields: [
{ title: 'Error', value: execution.error || 'Unknown' },
{ title: 'Duration', value: `${execution.duration}ms` }
]
}]
});
}
}Fazit
Task Scheduling in Node.js:
- node-cron: Einfach für Single-Server
- BullMQ: Distributed, Reliable, Scalable
- Serverless Cron: Vercel, AWS EventBridge
- Job Locking: Verhindert Duplikate
Zuverlässige Automation für jede Anwendung.
Bildprompts
- "Clock with automated tasks running at specific times, scheduling concept"
- "Multiple servers coordinating scheduled jobs, distributed cron"
- "Calendar with recurring events and checkmarks, automated workflow"