OqronKitOqronKit

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:

index.ts
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();
my-app/
├── src/
│   ├── jobs/           ← job definitions
│   │   ├── emails.ts
│   │   ├── billing.ts
│   │   └── crons.ts
│   └── index.ts        ← app entry point
└── package.json

Advanced: Conditional Registration

Because registration happens dynamically during file evaluation, you can conditionally load definitions based on process roles or environment variables:

index.ts
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:

FactoryModule 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' event

If 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

On this page