Job Registration
How OqronKit discovers and registers your job definitions — auto-discovery, explicit paths, and manual imports
Job Registration
Job Registration
OqronKit uses a self-registering factory pattern. Every time you call Queue(), Cron(), Worker(), Webhook(), RateLimit(), or Cache(), it immediately registers the definition in a module-global array. When oqron.start() is called, the Oqron engine drains these registries and starts processing them.
Because OqronKit does not perform filesystem auto-discovery scanning, you must explicitly import your definition files before calling oqron.start().
The Import Pattern
The recommended pattern is to group your background definitions in a folder like triggers/ or jobs/, and import them at the entry point of your application:
import { Oqron } from "oqronkit";
// 1. Import all definitions so they evaluate and register
import "./jobs/emails.js";
import "./jobs/billing.js";
import "./jobs/crons.js";
// 2. Instantiate and start Oqron
const oqron = new Oqron({
mode: "redis",
redis: "redis://localhost:6379",
});
await oqron.start();Recommended Directory Structure
my-app/
├── src/
│ ├── jobs/ ← job definitions
│ │ ├── emails.ts
│ │ ├── billing.ts
│ │ └── crons.ts
│ └── index.ts ← app entry point
└── package.jsonAdvanced: Conditional Registration
Because registration happens dynamically during file evaluation, you can conditionally load definitions based on process roles or environment variables:
import { Oqron } from "oqronkit";
// Common queues available on all nodes
import "./jobs/emails.js";
// Only load heavy compute jobs on worker nodes
if (process.env.NODE_ROLE === "worker") {
await import("./jobs/video-processing.js");
}
// Only load scheduler definitions on the scheduler node
if (process.env.NODE_ROLE === "scheduler") {
await import("./jobs/crons.js");
}
const oqron = new Oqron({
mode: "redis",
redis: "redis://...",
});
await oqron.start();How It Works Under the Hood
Each factory function (e.g. Queue(...)) pushes its config into a module-scoped registry array:
| Factory | Module Registry |
|---|---|
Queue() | pending array in queue/registry.ts |
Worker() | pending array in worker/registry.ts |
Cron() | pending array in scheduler/registry.ts |
Schedule() | pending array in scheduler/registry-schedule.ts |
Webhook() | pending array in webhook/registry.ts |
RateLimit() | pending array in ratelimit/registry.ts |
Cache() | pending array in cache/registry.ts |
Start-up and Init Sequence
oqron.start()
│
├─ 1. Initialize storage/broker/lock persistence layer
├─ 2. Instantiate module engines for all registered handles
│ └─ Each engine drains its corresponding global pending array
├─ 3. Run init() on all modules (sets up initial state, e.g. pauses)
├─ 4. Run start() on all modules (scheduler ticks start, workers begin polling)
└─ 5. Emit 'system:ready' eventIf a definition is registered for a module that is explicitly disabled in the
configuration (e.g. disableModules: ['cron']), it will remain in the
registry but the engine will never drain it or process it.
HMR & Deduplication
Each registry deduplicates by name. If a definition with the same name is registered twice (common during Hot Module Replacement (HMR) or hot reloading in development), the last registration wins:
// First registration
const q1 = Queue({ name: "emails", handler: handlerV1 });
// Second registration (e.g., HMR reload) — overwrites the first
const q2 = Queue({ name: "emails", handler: handlerV2 });
// Only handlerV2 will be used