← All @molecule/* packages · App templates

@molecule/api-cron

Core interface · cron · API (Node) · v1.0.1 · Apache-2.0

Cron scheduling core interface for molecule.dev — schedule, pause, resume, and manage recurring jobs

npm install @molecule/api-cron

npm · Source on GitHub

How it works

@molecule/api-cron is the cron core interface on the API (Node) side: the API your app calls, with no vendor inside.

Choose the implementation by bonding one of its 2 providers: @molecule/api-cron-bullmq, @molecule/api-cron-node-cron.

import { setProvider, schedule, list, close } from '@molecule/api-cron'
import { provider as nodeCron } from '@molecule/api-cron-node-cron'

setProvider(nodeCron)

const jobId = await schedule('cleanup', '0 3 * * *', async () => {
  console.log('Running nightly cleanup...')
})

const jobs = await list()

// On graceful shutdown / test teardown:
await close()

Providers (2): @molecule/api-cron-bullmq, @molecule/api-cron-node-cron

Works with: @molecule/api-bond, @molecule/api-i18n

Reference

Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit src/index.ts JSDoc, not this file.

Provider-agnostic cron scheduling interface for molecule.dev.

Defines the CronProvider interface for scheduling, pausing, resuming, cancelling, and manually triggering cron jobs. Bond packages (node-cron, BullMQ, etc.) implement this interface. Application code uses the convenience functions (schedule, cancel, list, pause, resume, runNow, close) which delegate to the bonded provider.

Quick Start

import { setProvider, schedule, list, close } from '@molecule/api-cron'
import { provider as nodeCron } from '@molecule/api-cron-node-cron'

setProvider(nodeCron)

const jobId = await schedule('cleanup', '0 3 * * *', async () => {
  console.log('Running nightly cleanup...')
})

const jobs = await list()

// On graceful shutdown / test teardown:
await close()

Type

core

Installation

npm install @molecule/api-cron @molecule/api-bond @molecule/api-i18n

API

Interfaces

CronConfig

Configuration options for cron providers.

interface CronConfig {
  /** Default timezone for all jobs. */
  timezone?: string
}

CronJob

A scheduled cron job.

interface CronJob {
  /** Unique job identifier. */
  id: string

  /** Human-readable job name. */
  name: string

  /** The cron expression (e.g., `'0 * * * *'` for every hour). */
  cron: string

  /** Current job status. */
  status: CronJobStatus

  /** Timestamp of the last execution, if any. */
  lastRun?: Date

  /** Timestamp of the next scheduled execution, if any. */
  nextRun?: Date

  /** Total number of times the job has been executed. */
  runCount: number
}

CronOptions

Options for scheduling a cron job.

interface CronOptions {
  /** IANA timezone for the cron schedule (e.g., `'America/New_York'`). */
  timezone?: string

  /** Whether to run the job immediately on creation. Defaults to `false`. */
  runOnInit?: boolean

  /** Maximum number of times the job should run. `undefined` means unlimited. */
  maxRuns?: number

  /** Start date — the job will not run before this date. */
  startDate?: Date | string

  /** End date — the job will not run after this date. */
  endDate?: Date | string

  /**
   * When `true`, a tick that arrives while the previous execution of this
   * job is still running is skipped instead of running concurrently.
   * Defaults to `false` (current behavior: overlapping executions are
   * allowed) to avoid changing existing deployments' concurrency.
   * Support is provider-specific: node-cron enforces it natively (skips the
   * tick and logs a warning); the BullMQ bond emulates it per-process by
   * skipping a tick while a previous invocation on the same worker process
   * hasn't finished — it does NOT coordinate across multiple distributed
   * worker processes.
   */
  noOverlap?: boolean
}

CronProvider

Cron provider interface.

All cron providers must implement this interface. Bond packages (node-cron, BullMQ, etc.) provide concrete implementations.

interface CronProvider {
  /**
   * Schedules a new cron job.
   *
   * @param name - Human-readable name for the job.
   * @param cron - A cron expression defining the schedule.
   * @param handler - The async function to execute on each tick.
   * @param options - Optional scheduling configuration.
   * @returns The unique job identifier.
   */
  schedule(
    name: string,
    cron: string,
    handler: () => Promise<void>,
    options?: CronOptions,
  ): Promise<string>

  /**
   * Cancels and removes a scheduled job.
   *
   * @param jobId - The job identifier.
   */
  cancel(jobId: string): Promise<void>

  /**
   * Lists all registered cron jobs.
   *
   * @returns An array of cron job descriptors.
   */
  list(): Promise<CronJob[]>

  /**
   * Pauses a running cron job without removing it.
   *
   * @param jobId - The job identifier.
   */
  pause(jobId: string): Promise<void>

  /**
   * Resumes a previously paused cron job.
   *
   * @param jobId - The job identifier.
   */
  resume(jobId: string): Promise<void>

  /**
   * Triggers immediate execution of a job regardless of its schedule.
   *
   * @param jobId - The job identifier.
   */
  runNow(jobId: string): Promise<void>

  /**
   * Releases the provider's resources (timers, queue/worker connections) so
   * the process can exit cleanly — required for graceful shutdown and tests.
   * Optional: in-process providers with no persistent resources may omit it.
   */
  close?(): Promise<void>
}

Types

CronJobStatus

Status of a cron job.

type CronJobStatus = 'active' | 'paused' | 'completed' | 'failed'

Functions

cancel(jobId)

Cancels and removes a scheduled job.

function cancel(jobId: string): Promise<void>
  • jobId — The job identifier.

Returns: Resolves when the job is cancelled.

close()

Releases the bonded provider's resources (timers, queue/worker connections) so the process can exit cleanly. Call during graceful shutdown and in test teardown. A no-op when the bonded provider does not implement close() (in-process providers with no persistent resources may omit it).

function close(): Promise<void>

Returns: Resolves when cleanup completes.

getProvider()

Retrieves the bonded cron provider, throwing if none is configured.

function getProvider(): CronProvider

Returns: The bonded cron provider.

hasProvider()

Checks whether a cron provider is currently bonded.

function hasProvider(): boolean

Returns: true if a cron provider is bonded.

list()

Lists all registered cron jobs.

function list(): Promise<CronJob[]>

Returns: An array of cron job descriptors.

pause(jobId)

Pauses a running cron job without removing it.

function pause(jobId: string): Promise<void>
  • jobId — The job identifier.

Returns: Resolves when the job is paused.

resume(jobId)

Resumes a previously paused cron job.

function resume(jobId: string): Promise<void>
  • jobId — The job identifier.

Returns: Resolves when the job is resumed.

runNow(jobId)

Triggers immediate execution of a job regardless of its schedule.

function runNow(jobId: string): Promise<void>
  • jobId — The job identifier.

Returns: Resolves when the job execution completes.

schedule(name, cron, handler, options)

Schedules a new cron job.

function schedule(
  name: string,
  cron: string,
  handler: () => Promise<void>,
  options?: CronOptions,
): Promise<string>
  • name — Human-readable name for the job.
  • cron — A cron expression defining the schedule.
  • handler — The async function to execute on each tick.
  • options — Optional scheduling configuration.

Returns: The unique job identifier.

setProvider(provider)

Registers a cron provider as the active singleton. Called by bond packages during application startup.

function setProvider(provider: CronProvider): void
  • provider — The cron provider implementation to bond.

Available Providers

ProviderPackage
Cron@molecule/api-cron-bullmq
Cron@molecule/api-cron-node-cron

Injection Notes

Requirements

Peer dependencies:

  • @molecule/api-bond ^1.0.1
  • @molecule/api-i18n ^1.0.1

Runtime Dependencies

  • @molecule/api-bond
  • @molecule/api-i18n

close() releases the bonded provider's resources (timers, queue/worker connections) and is a no-op if the provider doesn't implement it — call it during shutdown instead of reaching for getProvider().close?.() directly. CronOptions.noOverlap (per-job, default false) skips a tick that arrives while the previous run of the same job is still executing; support and cross-process coordination are provider-specific — see each bond's module docs.

E2E Tests

Integration checklist — drive the real flow (no mocks), adapt each item to this app's actual scheduled jobs, and check every box off one by one. A box you can't check is an integration bug to fix — not a skip:

  • Every job the app defines is REGISTERED at startup: after bootstrap, list() returns each one (or its schedule() call ran without error) — a job that never registers never fires.
  • Each job's REAL side effect happens when it runs — the digest row is written, the cleanup deletes, the report is generated. Trigger it now with runNow(jobId) (or invoke the handler directly) and assert the effect; never stub the body. COUNTERPARTY: the sandbox process is short-lived, so a real timed tick may never arrive — that is expected. Verify by direct invocation, not by waiting minutes for the schedule to fire.
  • Re-running a job is safe: invoke it twice and confirm no double effect (no double-charge, double-send, or duplicate row) — the handler is idempotent or guards its own re-entry.
  • A failing job is observable, not swallowed: force the handler to throw and confirm the error is logged/surfaced and the job's status reflects it.
  • The cadence is correct: read each job's cron expression and confirm it matches the intended schedule (nightly, hourly, …) — verify by reading it, not by waiting for a tick.
  • Any user-facing trigger is locked down: if the app exposes a manual "run now" or schedule-management endpoint, only an authorized caller can hit it — an anonymous request can't fire jobs or register arbitrary schedules.