nvoke

Use case

Cron jobs, honest ones.

You have a task that needs to run every hour, every Monday, or at 3 AM on the first of the month. nvoke gives you a real scheduler — not a best-effort GitHub Actions schedule or a cron tab on a VPS you forget to patch.

// schedule: 0 9 * * 1   (Mondays at 09:00 UTC)
export default async function () {
  const rows = await db.query(
    "select count(*) from signups where created_at > now() - interval '7 days'"
  );
  await sendEmail({
    to: "team@example.com",
    subject: "Weekly signups",
    body: `${rows[0].count} new signups this week.`
  });
  return { ok: true };
}
A weekly report job, running on a cron.

The usual suspects

The workloads we see most often as scheduled functions: daily digest emails, weekly team reports, nightly data reconciliation, polling an upstream API for changes, rolling keys or tokens, warming caches, dispatching reminder notifications, backing up one system to another. The shared property is that it has to happen on time, but it does not have to run continuously.

Why not just use GitHub Actions?

You can, and for very simple tasks it is fine. But the Actions scheduler is best-effort: your 5-minute cron will often be 10-30 minutes late during high platform load. You also pay for the full job runner startup (usually 20+ seconds) every tick, which gets expensive if you are polling frequently.

nvoke fires scheduled functions with sub-second latency off the tick, and a scheduled invocation costs the same as an HTTP one. If your cron needs to be on time — for billing, for SLA compliance, for anything where late is a bug — use a real scheduler.

Designing idempotent jobs

Any cron job will eventually run twice in a row for the same window. Either you deploy during a tick, or the platform retries, or someone manually replays a past invocation. Write the job so running it twice produces the same end state as running it once: use upsert instead of insert, track "last processed up to" timestamps, tag outgoing notifications with a dedupe key.

Schedule your first job.

Write the function. Pick a cadence. nvoke takes it from there.