Skip to main content
Run periodic tasks: clean expired data, sync records, generate reports. Your function fires automatically according to the schedule you configure. Pattern: config.cron + isCron guard + secrets for external connections.
index.ts
import { define, z } from "@jelou/functions";

export default define({
  name: "cleanup-job",
  description: "Cleans expired sessions and syncs records",
  input: z.object({}),
  config: {
    cron: [
      { expression: "0 3 * * *", timezone: "UTC" },
      { expression: "0 15 * * 1-5", timezone: "America/Guayaquil" },
    ],
    mcp: false,
  },
  handler: async (_input, ctx) => {
    if (!ctx.isCron) {
      return { skipped: true, reason: "only runs via cron" };
    }

    ctx.log("Running cleanup", { cron: ctx.trigger.cron });

    const dbUrl = ctx.env.get("DATABASE_URL");
    const apiKey = ctx.env.get("EXTERNAL_API_KEY");

    const staleRecords = await fetch(`${dbUrl}/api/sessions?expired=true`)
      .then((r) => r.json());

    let cleaned = 0;
    for (const record of staleRecords) {
      await fetch(`${dbUrl}/api/sessions/${record.id}`, {
        method: "DELETE",
        headers: { Authorization: `Bearer ${apiKey}` },
      });
      cleaned++;
    }

    ctx.log("Cleanup completed", { cleaned, total: staleRecords.length });
    return { cleaned, checked: staleRecords.length };
  },
});

Configure secrets

jelou secrets set cleanup-job DATABASE_URL=https://db.example.com EXTERNAL_API_KEY=YOUR_API_KEY

Why it works this way

  • config.cron defines when it runs. You can have multiple schedules with different timezones.
  • The guard if (!ctx.isCron) prevents someone from running the cleanup via HTTP accidentally.
  • config.mcp: false — a maintenance task is not an AI tool.
  • ctx.trigger.cron tells you which schedule triggered the execution.