
Security News
Ruby's Bundler 4.0.18 Extends Cooldown to bundle lock and bundle cache
The supply chain control that delays freshly published gems now covers lockfile generation and gem vendoring in Ruby projects.
Cron jobs for Next.js. Serverless-native.
Zero runtime dependencies. TypeScript-first. Works with Vercel Cron out of the box.
npm install croncall
// lib/jobs.ts
import { createClockTower } from "croncall";
export const tower = createClockTower({
jobs: {
syncUsers: {
schedule: "0 * * * *", // every hour
handler: async () => {
await db.syncUsersFromExternalAPI();
},
description: "Sync users from external API",
retry: { maxAttempts: 3, backoff: "exponential" },
timeout: 30_000,
},
sendDigest: {
schedule: "0 9 * * 1", // Mondays at 9 AM UTC
handler: async () => {
await email.sendWeeklyDigest();
},
description: "Send weekly digest email",
},
cleanupSessions: {
schedule: "@daily",
handler: async () => {
await db.deleteExpiredSessions();
},
},
},
secret: process.env.CRON_SECRET,
});
// app/api/cron/route.ts
import { createCronHandler } from "croncall/next";
import { tower } from "@/lib/jobs";
export const GET = createCronHandler(tower);
Add cron schedules to vercel.json:
{
"crons": [
{ "path": "/api/cron?job=syncUsers", "schedule": "0 * * * *" },
{ "path": "/api/cron?job=sendDigest", "schedule": "0 9 * * 1" },
{ "path": "/api/cron?job=cleanupSessions", "schedule": "0 0 * * *" }
]
}
Or generate it programmatically:
import { generateVercelCron } from "croncall/next";
import { tower } from "./lib/jobs";
console.log(JSON.stringify(generateVercelCron(tower, "/api/cron"), null, 2));
Each job has:
| Field | Type | Required | Description |
|---|---|---|---|
schedule | string | Yes | Cron expression or shortcut |
handler | () => Promise<void> | Yes | Async function to execute |
description | string | No | Human-readable description |
retry | { maxAttempts, backoff, baseDelay? } | No | Retry on failure |
timeout | number | No | Max execution time in ms |
Standard 5-field cron expressions:
┌───────────── minute (0-59)
│ ┌───────────── hour (0-23)
│ │ ┌───────────── day of month (1-31)
│ │ │ ┌───────────── month (1-12)
│ │ │ │ ┌───────────── day of week (0-6, Sun=0)
│ │ │ │ │
* * * * *
Supported features:
*1-51,3,5*/15, 1-30/2jan, feb, ..., decsun, mon, ..., satShortcuts:
| Shortcut | Equivalent |
|---|---|
@hourly | 0 * * * * |
@daily | 0 0 * * * |
@midnight | 0 0 * * * |
@weekly | 0 0 * * 0 |
@monthly | 0 0 1 * * |
@yearly | 0 0 1 1 * |
@annually | 0 0 1 1 * |
Clocktower is designed to work with Vercel Cron Jobs.
Vercel sends a CRON_SECRET environment variable and includes it in the Authorization: Bearer <secret> header. Clocktower validates this automatically:
options.secret passed to createCronHandlerconfig.secret from createClockTowerprocess.env.CRON_SECRETIf no secret is configured, requests are allowed without authentication.
import { generateVercelCron } from "croncall/next";
import { tower } from "./lib/jobs";
const crons = generateVercelCron(tower, "/api/cron");
// [{ path: "/api/cron?job=syncUsers", schedule: "0 * * * *" }, ...]
Run a specific job on demand:
const result = await tower.run("syncUsers");
console.log(result);
// { success: true, duration: 1234 }
Run all due jobs:
const results = await tower.runDue();
for (const [name, result] of results) {
console.log(`${name}: ${result.success ? "ok" : result.error}`);
}
Via HTTP (useful for testing):
# Run a specific job
curl http://localhost:3000/api/cron?job=syncUsers \
-H "Authorization: Bearer your-secret"
# Run all due jobs
curl http://localhost:3000/api/cron \
-H "Authorization: Bearer your-secret"
const schedule = tower.schedule();
// [
// { jobName: "syncUsers", nextRun: 2026-03-26T15:00:00.000Z, schedule: "0 * * * *" },
// { jobName: "sendDigest", nextRun: 2026-03-30T09:00:00.000Z, schedule: "0 9 * * 1" },
// ]
Configure retries per job:
{
retry: {
maxAttempts: 3, // retry up to 3 times after initial failure
backoff: "exponential", // or "linear"
baseDelay: 1000, // 1s base delay (default)
}
}
The JobResult includes retryCount when retries were attempted:
const result = await tower.run("syncUsers");
if (!result.success) {
console.error(`Failed after ${result.retryCount} retries: ${result.error}`);
}
All types are exported:
import type {
CronExpression,
JobDefinition,
JobRegistry,
ClockTowerConfig,
ClockTower,
JobResult,
JobExecution,
ScheduleEntry,
RetryConfig,
} from "croncall";
Job names are fully typed:
const tower = createClockTower({
jobs: {
syncUsers: { schedule: "@hourly", handler: async () => {} },
},
});
tower.run("syncUsers"); // OK
tower.run("nonexistent"); // Type error
MIT
FAQs
Cron jobs for Next.js. Serverless-native.
The npm package croncall receives a total of 17 weekly downloads. As such, croncall popularity was classified as not popular.
We found that croncall demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
The supply chain control that delays freshly published gems now covers lockfile generation and gem vendoring in Ruby projects.

Security News
During a UK cyber test, a Mythos 5 agent used sockpuppets, social engineering, and prompt injection to try to get a maintainer to merge malware.

Company News
Socket is now in the AWS Security Hub Extended plan. Adopt it through AWS, apply committed spend, and block malicious open source packages.