Spraxium logoSpraxium

Schedule Overview

The schedule module runs background jobs using cron expressions, intervals, and timeouts. Learn the job types, runtime lifecycle, and how the scheduler integrates with the framework.

What the schedule module solves

Most Discord bots need to run tasks on a timer. Clearing expired warnings, syncing member counts, posting daily summaries, and rotating status messages are all operations that happen independently of user input. The schedule module provides a decorator-driven API for defining these background jobs so you can declare timing logic alongside your service methods instead of managing raw timers.

The module supports four job types that cover different timing patterns. Cron jobs run on calendar-based schedules using standard cron expressions. Intervals repeat at fixed millisecond offsets. Timeouts fire once after a delay. After-online jobs fire once after the bot has fully connected to Discord. Together, these four types handle every common scheduling scenario.

The schedule module is a separate package (@spraxium/schedule) that plugs into the framework through the standard module system. It scans your providers for decorated methods at boot time, registers them in a central registry, and manages their lifecycle automatically. You do not need to manually start or stop timers.

When scheduling is the right tool

Use the schedule module when work should happen because of time, not because of a user action or Discord event. Cleanup jobs, refresh cycles, reminders, periodic syncs, and post-startup tasks are the usual fit.

If a task should only happen after a user explicitly asks for it, keep that logic in a command or interaction handler instead of forcing it into a scheduled job. Time-based automation and user-invoked flows are easier to maintain when they stay separate.

Job types at a glance

Each job type maps to a decorator that you place on a method inside an injectable service. The decorator tells the scheduler when and how to run that method. All four decorators accept an options object for naming, disabling, and controlling startup behavior.

DecoratorTriggerRepeatsUse case
@CronCron expressionYesCalendar-based recurring tasks
@IntervalFixed millisecondsYesPolling, heartbeats, periodic syncs
@TimeoutDelay in millisecondsNoOne-time delayed initialization
@AfterOnlineDelay after bot readyNoPost-connection setup tasks

Runtime lifecycle

The schedule module operates in two phases that align with the framework boot sequence. Understanding these phases helps you predict when each job type starts executing and why @AfterOnline exists as a separate decorator instead of being a cron variant.

Boot phase

At application startup, the framework scans all providers and registers every method decorated with @Cron, @Interval, or @Timeout. Jobs with runOnInit enabled execute immediately during this phase. Interval and timeout timers begin counting from this moment.

Ready phase

After the Discord client emits its ready event, the framework registers all @AfterOnline jobs and starts their delay timers. The separation exists because some tasks need an active Discord connection, like fetching guild data or sending welcome messages. Running these during boot would fail because the client is not connected yet.

Runtime management

Once all jobs are registered, the ScheduleService provides a public API for pausing, resuming, destroying, and manually triggering jobs by name. You can inject ScheduleService into any provider to control jobs programmatically from slash commands, prefix commands, or HTTP endpoints.

Minimum setup

To use the schedule module, install the @spraxium/schedule package, create a configuration file using defineSchedule, and register ScheduleModule in your application. The module is global by default, so you only need to register it once. Any provider in any module can then use the schedule decorators.

config/schedule.config.ts
import { defineSchedule } from '@spraxium/schedule';

export default defineSchedule({
  timezone: 'America/Sao_Paulo',
});

The schedule module uses the MemoryDriver by default, which stores job state in memory. For distributed bots running multiple shards or processes, switch to the RedisScheduleDriver to prevent duplicate executions.

Operational guidance

Scheduled jobs should be written with idempotency in mind. If a process restarts or a distributed driver retries coordination, a well-designed job should be safe to run twice without corrupting state.

It is also a good habit to give important jobs explicit names through decorator options. Named jobs are easier to inspect, pause, resume, or trigger manually through the schedule service later.

Project structure

A typical bot with scheduled tasks organizes them as service providers inside feature modules. Each service groups related jobs, and the configuration file lives in the project config directory alongside prefix and other plugin configs.

project structure
schedule.config.ts
tasks.module.ts
cleanup.service.ts
sync.service.ts