Background jobs
Workers, the job flow, and why the workers are Python.
Workers
Long-running AI tasks shouldn't block the web request. The jobs/ directory integrates task queues like Celery or Dramatiq.
1jobs/
2|-- __init__.py
3`-- celery/
4 |-- __init__.py
5 `-- celery_app.py # the Celery app + broker wiring
1import os
2from celery import Celery
3
4celery_app = Celery(
5 "{{appName}}",
6 broker=os.environ["CELERY_BROKER_URL"],
7)
8
9celery_app.autodiscover_tasks()
The system is chosen in bionicjs.config.ts under the jobs key. The system accepts a broker URL; RQ is Redis-only by design, while Celery and Dramatiq take Redis or RabbitMQ.
The flow
When a user triggers an AI generation in the React frontend, the request goes to Nitro, which enqueues a job in Redis. The Python worker picks it up, processes it, and updates a database or sends a WebSocket message back to the client upon completion.
1React button
2 | api.generateReport() # typed RPC stub
3 v
4Nitro handler
5 | jobs.enqueue("generate", { ... })
6 v
7Redis queue
8 ^
9 | picks up the task
10Python worker
11 | runs the job
12 | writes result / emits WebSocket event
13 v
14React receives the result
The enqueue client becomes the jobs export on @bionicjs/core/server, so any Nitro handler can enqueue work:
1import { jobs } from "@bionicjs/core/server";
2
3export default defineEventHandler(async (event) => {
4 const body = await readBody(event);
5 await jobs.enqueue("generate.report", { userId: body.userId });
6 return { queued: true };
7});
Why Python workers
Jobs exist because of the same honest baseline as the rest of BionicJS: the mature workers are Python. Celery, RQ, and Dramatiq are battle-tested, and the AI work they run lives in the Python ecosystem.
| System | Broker | Notable |
|---|---|---|
Celery | Redis or RabbitMQ | Autodiscovery, task routing |
RQ | Redis only | The simplest entry point |
Dramatiq | Redis or RabbitMQ | Lightweight, actor-style |
jobs/ plus one enqueue call in a handler - a sync boundary, not a new architecture. Deploy the worker alongside the Python process using the generated configurations.22 / 25