@talosjs/cron component runs background tasks on top of Bun’s native Bun.cron. You extend the abstract Cron class, declare a schedule with a readable string like "every 1 hours", and implement a handler() that runs on that schedule. Behind the scenes the schedule is converted to a crontab expression, so there is no external scheduler to operate.
What it does
Schedules read like English. You write"every 5 minutes" or "in 30 seconds" and the component converts that to crontab syntax for you. Each job is a class with three abstract methods (getTime(), getTimeZone(), and handler()). getTimeZone() is part of the contract, but the current Cron base class does not pass a timezone into Bun.cron; schedules therefore run in the server’s local time. You drive a job with start(), stop(), and isActive(), and you register it with @decorator.cron() so the container can resolve it.
How it works
A cron job is a class that extendsCron and implements its three abstract methods. When you call start(), the schedule from getTime() is converted to a crontab expression and registered with Bun.cron, which invokes your handler() on every tick.
The schedule string has the shape
"<prefix> <number> <unit>":
So
"every 5 minutes" runs repeatedly every five minutes. The in prefix is converted into a concrete crontab expression for the computed future date and time; because Bun.cron is recurring, it is not a true one-shot timer. An invalid format or a non-positive number throws CronException.
Decorator and usage
@decorator.cron()
Registers a cron class with the container. It accepts an optional scope (defaults to singleton). Decorate any class that extends Cron.
getTimeZone() is still part of the interface, but the current base class does not apply it when starting the job:
in prefix only when you want the helper to compute the next matching calendar time from a delay:
Exceptions
The component throwsCronException when a schedule is malformed or a job fails to start. It carries a machine-readable key, a human-readable message, and a data object.
Running jobs reliably
Catch errors insidehandler(). A throw there should not take down the scheduler, so wrap risky work and log what fails. Make handlers idempotent, since a run may overlap or repeat and the work has to stay safe to execute more than once. Match the interval to the work too: a long task on "every 1 minutes" can pile up if it can’t finish before the next tick.
For time-sensitive jobs like reports and billing, remember that the current base class runs on the server’s local time even if getTimeZone() returns a value. Inject caches, databases, and loggers from the container rather than constructing them inside the handler. Use every for recurring work; in is only a scheduling convenience for computing a future crontab expression, not a true one-shot timer. And stop jobs you no longer need: stop() releases the timer, and isActive() tells you whether a job is still running.
CLI command
Scaffold a cron class and its test file with the generator. It writes the class undermodules/<module>/src/crons/<Name>Cron.ts, registers it in the module’s cronJobs array, and installs @talosjs/cron if it is missing.
The generated class is a ready-to-fill stub with the schedule, timezone, and handler in place:
Use with Claude and Codex
The generator ships a matchingcron:create skill. It runs the scaffold and then guides your AI agent through completing the job: setting the schedule in getTime(), choosing a timezone, and implementing handler() with real logic. Initialize the skills once for your agent:
- Claude
- Codex
Prompt
cron:create --name=CleanExpiredSessions, then sets the schedule and implements the handler() to remove expired sessions.