Skip to main content

TL;DR

BullMQ is a Redis queue library. OpenWorkflow is a durable workflow engine. If your flow is a chain of dependent jobs with delays and retries, you usually end up assembling workflow behavior on top of queue primitives. OpenWorkflow provides that behavior directly, backed by PostgreSQL or SQLite.

What This Looks Like in Practice

With BullMQ, a multi-step process means Flows, parent/child job dependencies, manual state tracking, and delayed jobs. With OpenWorkflow, you write one function:
import { defineWorkflow } from "openworkflow";

export const processOrder = defineWorkflow(
  { name: "process-order" },
  async ({ input, step }) => {
    // validate the order, charge the payment, and fulfill the order
    const order = await step.run({ name: "validate-order" }, async () => {
      return await orders.validate(input.orderId);
    });

    await step.run({ name: "charge-payment" }, async () => {
      await payments.charge(order.paymentMethod, order.total);
    });

    await step.run({ name: "fulfill-order" }, async () => {
      await warehouse.ship(order.id, order.shippingAddress);
    });

    // wait 7 days for delivery, then ask for a review
    await step.sleep("wait-for-delivery", "7d");

    await step.run({ name: "request-review" }, async () => {
      await email.send({ to: order.email, template: "review-request" });
    });
  },
);
Each step.run is a durable checkpoint. If the process crashes after charging payment, the workflow resumes from that point and does not re-run completed steps.

When OpenWorkflow is the Better Fit

Native workflow semantics

BullMQ is strong for queue processing, but complex business flows require composing jobs into graphs and managing orchestration state yourself. OpenWorkflow treats the flow as one workflow run with durable step history and replay.

No separate queue infrastructure

BullMQ requires Redis, a separate service to run alongside your application. OpenWorkflow uses PostgreSQL or SQLite, which most teams already have for their application data. One fewer service to provision and monitor.

When BullMQ is the Right Call

  • Simple fire-and-forget jobs: If you’re sending emails or processing uploads with no dependencies between jobs, a queue is the right tool. You don’t need workflows for that.
  • Existing Redis investment: If your team already runs Redis and your jobs are straightforward, the adoption cost of BullMQ is genuinely low.
  • High throughput on independent jobs: BullMQ on Redis can push very high job throughput for independent, short-lived work.