Background job scheduling is a planned feature for Baseflare that will be delivered in Phase 7b of the implementation roadmap. When implemented,Documentation Index
Fetch the complete documentation index at: https://mintlify.com/nickruigrok/baseflare/llms.txt
Use this file to discover all available pages before exploring further.
ctx.scheduler will be available inside mutations and actions, allowing you to schedule any internal function to run after a delay or at a specific future timestamp. Jobs are stored durably in a SchedulerDO Durable Object, executed via the Cloudflare Alarms API, and tracked through their full lifecycle from pending to succeeded or failed.
ctx.scheduler API
ctx.scheduler exposes two methods defined in the current type interfaces, with a third (cancel) planned for Phase 7b. Both runAfter and runAt are available in mutations and actions. Scheduling a job returns a job ID string that can be stored in your application data.
Scheduler interface is already defined in packages/baseflare/src/server/functions/types.ts with runAfter and runAt. The cancel method is planned as part of the Phase 7b runtime implementation:
The
cancel method is not yet present in the Scheduler interface. It will be added as part of Phase 7b: ctx.scheduler.cancel(jobId): Promise<void>.FunctionReference type used by both methods is a lightweight branded type defined in functions/types.ts:
internal.* references that satisfy this interface, giving you compile-time safety when passing function references to the scheduler.
How It Works
All scheduled job state lives insideSchedulerDO, a Durable Object that has its own internal SQLite database separate from your application D1. This means job history never competes with application queries and the scheduler can be independently inspected from the dashboard.
The execution lifecycle works as follows:
- When
ctx.scheduler.runAfter()orctx.scheduler.runAt()is called, the Worker sends the job toSchedulerDOvia a DO stub call. - The DO inserts the job into its internal SQLite with status
pendingand recalculates the next alarm time:SELECT MIN(execute_at) FROM jobs WHERE status = 'pending'. - The DO sets or updates a Cloudflare Alarm for that timestamp. The Alarms API has no time limit — a job scheduled 30 days from now will fire correctly.
- When the alarm fires, the DO queries all jobs where
execute_at <= now AND status = 'pending', sets each torunning, and calls the environment Worker’s internal function endpoint for each job. - The Worker executes the function handler and returns the result to the DO.
- The DO updates the job record to
succeeded(with acompleted_attimestamp) orfailed(with the error message stored). - After processing, the DO recalculates the next alarm for any remaining
pendingjobs.
canceled and recalculates the next alarm. A canceled job will not execute even if the alarm fires before the status update propagates.
Job History
Every job is recorded in theSchedulerDO internal SQLite with the following structure. The dashboard will be able to query this table to display job history filtered by status, function, or time range.
SchedulerDO:
Cron Triggers
Recurring jobs are handled separately from the scheduler. Baseflare plans to support recurring jobs through adefineCrons() function that maps cron expressions to internal function references. When you deploy, the CLI reads your crons configuration and creates or updates Cloudflare Cron Triggers for the Worker via the Cloudflare API.
scheduled event. Baseflare’s Worker runtime handles this event, looks up the matching handler from the crons manifest, and executes it. The defineCrons() function is planned as part of Phase 7b alongside ctx.scheduler and does not yet exist in any source file.
During local development, npx baseflare dev emulates cron triggers by parsing the defineCrons() output, using cron-parser to calculate next fire times, and calling the Miniflare Worker’s scheduled() method at the correct intervals.
No Time Limit
Only Call Internal Functions
Only
internal.* functions can be scheduled with ctx.scheduler.runAfter() and ctx.scheduler.runAt(). Public functions exposed via the api.* object are intentionally not schedulable. This prevents the scheduler from becoming a vector for unauthorized execution — if public functions could be scheduled directly, any code with access to ctx.scheduler could enqueue calls that normally require client authentication or permission checks. Internal functions are server-only and never exposed through the RPC routing layer.