Capabilities/Background jobs

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.

jobs/ after selecting a system
1jobs/
2|-- __init__.py
3`-- celery/
4    |-- __init__.py
5    `-- celery_app.py          # the Celery app + broker wiring
The worker app
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.

The enqueue-to-complete lifecycle
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:

server/api/generate.ts
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.

SystemBrokerNotable
CeleryRedis or RabbitMQAutodiscovery, task routing
RQRedis onlyThe simplest entry point
DramatiqRedis or RabbitMQLightweight, actor-style
ⓘ
A job is one function in 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.