On this page
scheduling and commands
introduction
dframework includes a built in task scheduler that lets you define recurring background tasks directly in your application code. instead of configuring system level cron jobs on your server, you define your schedule in a single file and the framework handles the rest. tasks can be references to command classes or inline functions.
the scheduler starts automatically when the application boots and runs for the lifetime of the process. each task is executed in the main thread synchronously in an isolated context and automatically rescheduled after completion.
defining schedules
the schedule file
your schedule is defined in console/Schedule.js at the root of your project. this file exports a default function that receives the Scheduler instance. you use the scheduler's command() method to register tasks and chain a frequency method.
1export default function (scheduler) {2 scheduler.command('CleanupExpiredSessions').daily();3 scheduler.command('SyncExternalData').everyFifteenMinutes();4 scheduler.command('GenerateSitemaps').weekly();5}the framework automatically detects this file on startup and begins executing the defined schedule.
scheduling commands
when you pass a string to scheduler.command(), the scheduler resolves it as a command file in your console/commands directory, checking both ${name}.js and ${name}Command.js. the file must export a default class with a handle() method.
1scheduler.command('PruneOldLogs').daily();2scheduler.command('Report').args(['--format=pdf', 'sales']).daily();this resolves console/commands/PruneOldLogs.js or console/commands/PruneOldLogsCommand.js, instantiates the class, and calls its handle() method at the specified interval. chaining .args(argsArray) forwards arguments and options to the command instance.
scheduling closures
for simple tasks that don't warrant a dedicated command file, you can pass an inline function directly.
1export default function (scheduler) {2 scheduler.command(async () => {3 const count = await Session.where('expires_at', '<', new Date()).delete();4 Log.info(`pruned ${count} expired sessions`);5 }).everyThirtyMinutes();6}
frequency options
preset intervals
the scheduler provides a set of human readable preset interval methods that you chain after command().
| method | interval |
|---|---|
everyMinute() |
60 seconds |
everyFiveMinutes() |
5 minutes |
everyTenMinutes() |
10 minutes |
everyFifteenMinutes() |
15 minutes |
everyThirtyMinutes() |
30 minutes |
hourly() |
1 hour |
daily() |
24 hours |
weekly() |
7 days |
1scheduler.command('HeartbeatCheck').everyMinute();2scheduler.command('CacheWarmer').hourly();3scheduler.command('WeeklyDigest').weekly();
custom intervals
for intervals not covered by the presets, use the every() method with a duration in milliseconds.
1scheduler.command('PollExternalApi').every(45000);2scheduler.command('RotateApiKeys').every(12 * 60 * 60 * 1000);intervals cannot exceed 2147483647 milliseconds (approximately 24.8 days) due to node 32 bit integer timeout limits. for intervals exceeding this duration (such as monthly or quarterly jobs), configure a system cron job to run the command on demand using dstrn run <command>.
creating commands
the command class
a command is a class stored in the console/commands directory. it must export a default class with an async handle(args, options) method. the handle method contains the logic that will be executed when the command runs.
1export default class PruneOldLogsCommand {2 async handle(args, options) {3 const cutoff = new Date();4 cutoff.setDate(cutoff.getDate() - 30);5 await DB.table('logs').where('created_at', '<', cutoff).delete();6 Log.info('old logs pruned');7 }8}scheduled command instances provide argument helpers (this.option(), this.hasOption(), this.argument(), this.options, this.arguments, this.rawArgs). commands triggered by the automated scheduler run with empty arguments (handle([], {})), while manual execution via dstrn run forwards arguments and options (handle(rawArgs, options)).
generating commands
you can scaffold a new command file using the cli.
1dstrn make:command PruneOldLogsthis creates console/commands/PruneOldLogs.js with the correct boilerplate structure.
running commands
you can execute any scheduled command on demand using the run cli command:
1dstrn run PruneOldLogs
when executed manually via dstrn run, additional arguments and options passed after the command name are forwarded to handle(args, options) and accessible through this.option() and this.argument(). when triggered by the automated scheduler, handle([], {}) runs with empty arguments. dframework reports elapsed execution time upon completion.
how the scheduler works
automatic startup
when the application starts, the framework checks for a console/Schedule.js file. if it exists, the framework imports it, passes a new Scheduler instance, and calls scheduler.start() after all tasks are registered. each registered task is scheduled to run after its configured interval elapses and then rescheduled continuously.
execution and rescheduling
when a task's interval elapses, the scheduler calls the task's handler (either a command class or an inline function). after the task completes, the scheduler calculates the next execution time by subtracting the elapsed execution time from the interval, ensuring consistent spacing between runs even if the task takes a significant amount of time to complete. the next run is scheduled using setTimeout.
each task tracks its lastRun timestamp. the scheduler uses this to compute the appropriate delay before the next execution.
manual execution
you can programmatically trigger any registered scheduled task by name using scheduler.run().
1await app.scheduler.run('CleanupExpiredSessions');this immediately executes the task once on demand without modifying its scheduled recurrence timer.
error handling
errors thrown by scheduled tasks are logged and isolated. a failing task is automatically rescheduled without affecting other tasks or interrupting the server.
1[Scheduler] PruneOldLogs caused error: ER_NO_SUCH_TABLE: Table 'logs' doesn't existif the task itself causes an unexpected error during the scheduling phase, a separate catch logs it and prevents the error from propagating to the main process.

