Background Jobs (Cron)

The Background Jobs component provides a standardized way to schedule and manage recurring tasks in your ServerCN application using node-cron.

Background jobs are essential for tasks like:

  • Generating and sending daily reports.
  • Cleaning up temporary files or expired sessions.
  • Synchronizing data with third-party APIs.
  • Sending scheduled notifications or emails.

NPM Docs

Installation Guide

Install the component using the ServerCN CLI:

npx servercn-cli add background-jobs

This will install node-cron and its types, and create the basic jobs structure in your project.

Project Structure

Depending on your architecture, the jobs are organized as follows:

  • MVC: src/jobs/
  • Feature: src/shared/jobs/

Implementation

Create individual job files for different tasks to keep your code modular and readable.

File: src/jobs/example.job.ts

import cron from "node-cron";
 
/**
 * Tasks can be scheduled using standard cron syntax:
 * # ┌────────────── second (optional)
 * # │ ┌──────────── minute
 * # │ │ ┌────────── hour
 * # │ │ │ ┌──────── day of month
 * # │ │ │ │ ┌────── month
 * # │ │ │ │ │ ┌──── day of week
 * # │ │ │ │ │ │
 * # │ │ │ │ │ │
 * # * * * * * *
 */
 
export const dailyCleanupJob = cron.schedule(
  "0 0 * * *",
  () => {
    console.log("Running daily cleanup at midnight...");
    // Implement cleanup logic here
  },
  {
    scheduled: false, // Don't start automatically
    timezone: "UTC"
  }
);

Export a central initialization function to start all your jobs at once.

File: src/jobs/index.ts

import { dailyCleanupJob } from "./daily-cleanup.job";
import { reportGenerationJob } from "./report-generation.job";
 
export const initJobs = () => {
  dailyCleanupJob.start();
  reportGenerationJob.start();
 
  console.log("✅ All background jobs started successfully.");
};

Import and call the initJobs function in your main entry file (usually server.ts).

import app from "./app";
import { initJobs } from "./jobs";
 
const PORT = process.env.PORT || 5000;
 
app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
 
  // Start background jobs after server starts
  initJobs();
});

Cron Syntax Refresher

| Schedule               | Syntax       |
| :--------------------- | :----------- |
| Every minute           | `* * * * *`  |
| Every hour             | `0 * * * *`  |
| Every day at midnight  | `0 0 * * *`  |
| Every Sunday at noon   | `0 12 * * 0` |
| Every 1st of the month | `0 0 1 * *`  |

Real-World Examples

Here are some common scenarios where background jobs are used in production-grade Express applications.

src/cron/cleanup-refresh-tokens.cron.ts or src/jobs/cleanup-refresh-tokens.job.ts

import cron from "node-cron";
import { lt, or, eq } from "drizzle-orm";
import { refreshTokens } from "../drizzle";
import db from "../configs/db";
import { logger } from "../utils/logger";
 
export function startRefreshTokenCleanupJob() {
  cron.schedule(
    "0 2 * * *", // daily at 2 am
    async () => {
      try {
        const now = new Date();
 
        const [result] = await db
          .delete(refreshTokens)
          .where(
            or(
              lt(refreshTokens.expiresAt, now),
              eq(refreshTokens.isRevoked, true)
            )
          );
 
        logger.info(
          `Refresh token cleanup completed. Deleted ${result.affectedRows} records`
        );
      } catch (error) {
        logger.error(error, "Refresh token cleanup failed");
      }
    },
    {
      timezone: "Asia/Kathmandu"
    }
  );
}

Remove unverified users who haven't verified their email within 24 hours.

import cron from "node-cron";
import { users } from "../models/user.model"; // Or schema
 
export const cleanupUnverifiedUsers = cron.schedule("0 0 * * *", async () => {
  try {
    console.log("🧹 Starting unverified user cleanup...");
 
    const oneDayAgo = new Date(Date.now() - 24 * 60 * 60 * 1000);
 
    // Example deletion logic
    const result = await users.deleteMany({
      isEmailVerified: false,
      createdAt: { $lt: oneDayAgo }
    });
 
    console.log(`✅ Cleanup finished. Removed ${result.deletedCount} users.`);
  } catch (error) {
    console.error("❌ Cleanup job failed:", error);
  }
});

Send a heartbeat or log system performance metrics every 15 minutes.

import cron from "node-cron";
import os from "os";
 
export const systemMonitorJob = cron.schedule("*/15 * * * *", () => {
  const freeMem = os.freemem() / 1024 / 1024;
  const totalMem = os.totalmem() / 1024 / 1024;
  const usage = ((totalMem - freeMem) / totalMem) * 100;
 
  console.log(`📊 System Health: ${usage.toFixed(2)}% memory usage`);
 
  if (usage > 90) {
    // Trigger alert or notification
    console.warn("⚠️ High memory usage detected!");
  }
});

Fetch and cache currency exchange rates from an external API every hour.

import cron from "node-cron";
import axios from "axios";
 
export const syncExchangeRates = cron.schedule("0 * * * *", async () => {
  try {
    console.log("💱 Syncing exchange rates...");
    const { data } = await axios.get("https://api.exchangerate.host/latest");
 
    // Logic to store rates in Redis or Database
    // await cache.set("exchange_rates", JSON.stringify(data.rates));
 
    console.log("✅ Exchange rates updated successfully.");
  } catch (error) {
    console.error("❌ Exchange rate sync failed:", error);
  }
});

Best Practices

Always wrap your job logic in try-catch blocks to prevent an error in one job from crashing your entire Node.js process.

Use a proper logger (like Winston or Pino) inside your jobs to track execution history and troubleshoot failures.

If a job performs heavy database operations, ensure it doesn't block the main event loop. Consider offloading very large tasks to a separate worker process or a dedicated queue system (like BullMQ) if needed.

Ensure your jobs are idempotent—running the same job twice (due to a restart or overlap) should not cause data corruption or duplicate side effects.

Summary

The Background Jobs component using node-cron is a powerful and lightweight solution for adding scheduled tasks to your ServerCN application without the complexity of a full-blown message queue.

File & Folder Structure

ServerCN

Select a file to view its contents

Installation

npx servercn-cli add background-jobs