Cross-ecosystem interop
Run NestJS, Adonis, and Python workers on one durable control plane. The BullMQ transport is the shared wire, so a step dispatched by a NestJS engine can execute on a Python or Adonis worker and flow back.
@dudousxd/nestjs-durable, the Adonis port (@adonis-agora/durable), and the Python durable-worker client all speak the same wire protocol, byte-for-byte. A single control plane — in any of the three runtimes — can dispatch work to workers written in any of the others. Your NestJS engine can run a workflow whose steps execute on a Python worker; an Adonis control plane can hand a step to a NestJS worker.
This is proven with live bidirectional interop tests, not just a shared spec: a NestJS control plane dispatching to a store-less Adonis worker, and an Adonis control plane dispatching to a real Python durable-worker — across BullMQ major versions (bullmq 5.x on Node, 2.x on Python), results flowing back correctly.
The shared wire: BullMQ
Interop rides the BullMQ transport. The control-plane pub/sub channel and every cross-process DTO match field-for-field across the three SDKs; the transport adds the shared task/result queues and worker registry:
| Purpose | Channel | Mechanism |
|---|---|---|
| Task dispatch (step + workflow) | ${P}-tasks-${token} | BullMQ queue |
| Step result | ${P}-results | queue |
| Workflow decision | ${P}-decisions | queue |
| Control broadcast | ${P}-control | Redis pub/sub |
| Worker liveness | ${P}-worker-heartbeat:${token}:${instance} | Redis key, EX 35 |
| Worker descriptor | ${P}-worker-descriptor:${token}:${instance} | Redis key |
where P is the effective prefix and the routing token is <name>@<tenant> when a partition is set. Serialization is plain JSON with the DTO as the job data — the worker discriminates step vs workflow by shape. Dates are epoch-ms on cross-process DTOs (ISO only where nested in an EngineEvent/WorkflowRun), and BullMQ priority is inverted the same way on every side. The handshake descriptor's descriptorHash even agrees three-way (Python folds UTF-16 code units to match JS charCodeAt), so negotiation is cross-SDK.
Running a polyglot fleet
-
NestJS control plane — configure the BullMQ transport and dispatch a step by name:
DurableModule.forRoot({ topology: { role: 'control-plane' }, store, transport: new BullMQTransport({ connection: { host: '127.0.0.1', port: 6379 } }), }) -
Python worker — install the client (
pip install durable-worker[redis]) and register a handler under the same name your workflow calls withctx.step('py-echo', ...).run_redis_workerconsumes the same Redis queues the BullMQ transport dispatches to:import asyncio from durable_worker import Worker from durable_worker.redis_runner import run_redis_worker worker = Worker() @worker.step("py-echo") async def py_echo(data): return {"echoed": data} async def main(): await run_redis_worker(worker) await asyncio.Event().wait() asyncio.run(main()) -
Start a run. The
ctx.step('py-echo')task lands on${P}-tasks-py-echo, the Python worker consumes and executes it, publishes on${P}-results, and the NestJS run resumes with the Python-computed output. See Python workers for the full client.
Version safety across runtimes
Because every worker advertises a descriptor and the control plane negotiates before dispatching, a polyglot fleet is version-safe: an incompatible protocol major is refused loudly, an optional capability a Python worker lacks routes that work only to workers that have it, and a run requiring a capability no live worker provides parks blocked until one appears — never a silent cross-language hang.
Learn more
- Python workers — the
durable-workerclient (PyPI), authoring steps and coordinator-driven workflows in Python. - BullMQ / Redis transport — the shared wire in detail.
- Handshake & negotiation — the compatibility layer that makes a mixed-version fleet safe.
Handshake & negotiation
How a mixed fleet stays version-safe — workers advertise a capability descriptor, the control plane negotiates compatibility, and work routes only to workers that can run it. Runs park blocked instead of hanging.
Patterns
A cookbook of common durable-workflow shapes — human approval, webhook confirmation, batch fan-out, cron digests, booking sagas, rate-limited APIs, and long-lived pollers — each a short, self-contained recipe with a link to the full page.