Migrating from BullMQ
Concept mapping from BullMQ to the FlexiQ Node SDK.
Concept mapping from BullMQ to the FlexiQ Node SDK.
If you're coming from BullMQ, the moving parts map closely. The biggest shifts:
FlexiQ is storage-backed (SQLite, Postgres, or Redis — not Redis-only),
handlers are registered by name on the queue rather than wired to a separate
Worker processor, and orchestration is built in.
| BullMQ | FlexiQ |
|---|---|
new Queue(name, { connection }) | new Queue({ backend, dsn }) — one object is producer + admin |
new Worker(name, processor, { connection }) | queue.task(name, fn) to register, then queue.runWorker() |
queue.add("job", data, opts) | queue.task("job", fn) once, then queue.enqueue("job", args, opts) |
job.data | the positional args passed to the handler |
job.id, progress | currentJob() → jobId, setProgress() |
await job.waitUntilFinished(queueEvents) | await queue.result(id, { timeoutMs }) — defaults to 30s and throws ResultTimeoutError on timeout |
attempts + backoff | maxRetries + retryBackoff |
rateLimiter | per-task / per-queue rateLimit: "100/m" |
QueueEvents | queue.on(event, …) + middleware |
QueueScheduler | not needed — a running queue.runWorker() handles delayed and repeatable jobs itself |
| Repeatable jobs (cron) | queue.registerPeriodic(name, task, cron) |
| Flows (parent/child) | Workflows (DAG, fan-out, gates, saga) |
queue.getJobCounts() | await queue.stats() / statsAllQueues() |
| Bull Board | the built-in dashboard |
// BullMQ
const queue = new Queue("emails", { connection });
await queue.add("welcome", { userId: 1 });
new Worker("emails", async (job) => send(job.data.userId), { connection });
// FlexiQ
const queue = new Queue({ backend: "redis", dsn: "redis://localhost" });
queue.task("welcome", (userId: number) => send(userId));
queue.enqueue("welcome", [1], { queue: "emails" });
queue.runWorker({ queues: ["emails"] });enqueue infers argument types from the registered task; data
is positional args, not a single data object.registerPeriodic (cron) jobs
fire from the same queue.runWorker() poll loop that runs your tasks. If no
worker process is running, cron jobs simply don't fire — there's no
QueueScheduler-equivalent to run independently.Migrating incrementally? Point FlexiQ at the same Redis you already run, move one task family at a time, and keep both systems until cut over.