Skip to content

Framework integration

firm.contrib has optional glue for embedding firm-queue in a web app. Each piece is opt-in, behind its own extra, and nothing in core imports it — if you don't use it, it costs nothing. You still define jobs the normal way with @bq.job.

Import Install What it does
firm.contrib.fastapi.lifespan firm-queue[fastapi] a FastAPI lifespan that configures the queue (and optionally runs workers)
firm.contrib.flask.Firm firm-queue[flask] a Flask extension + a flask firm worker command
firm.contrib.sqlalchemy.enqueue_after_commit — (SQLAlchemy is core) enqueue only when a session commits

FastAPI

from fastapi import FastAPI
from firm.contrib.fastapi import lifespan

app = FastAPI(lifespan=lifespan(database_url="postgresql://localhost/app"))

@app.post("/welcome/{user_id}")
def welcome(user_id: int):
    send_welcome.enqueue(user_id)      # a normal @bq.job
    return {"queued": True}

The lifespan calls configure(...) on startup so your handlers can enqueue. Pass embed_workers=True (with queues=, threads=) to also run a worker + dispatcher inside the app process — convenient for development or a single-process deploy; it's stopped on shutdown.

Flask

from flask import Flask
from firm.contrib.flask import Firm

app = Flask(__name__)
app.config["FIRM_DATABASE_URL"] = "postgresql://localhost/app"
Firm(app)                         # or Firm(app, database_url="...")

@app.post("/welcome/<int:user_id>")
def welcome(user_id):
    send_welcome.enqueue(user_id)
    return "", 202

The extension configures the queue and registers a CLI group, so you run workers with:

flask firm worker --threads 5 --queues default,mailers

Firm(app, embed_workers=True) runs the worker inside the web process instead (dev / single-process only — otherwise every web worker starts its own supervisor).

Transactional enqueue

enqueue_after_commit defers an enqueue until your SQLAlchemy session commits, and drops it on rollback — so you never enqueue a job for a request that didn't persist:

from firm.contrib.sqlalchemy import enqueue_after_commit

def create_order(session, payload):
    order = Order(**payload)
    session.add(order)
    enqueue_after_commit(session, charge_card, order.id)   # fires iff the commit succeeds
    session.commit()

The job is enqueued in firm's own transaction just after your commit — so it's "enqueue iff the request committed," not one atomic transaction. A crash in the narrow window between the two commits could still drop the enqueue; for most apps that's the right trade.

Production shape

Embed workers for dev; in production run them as separate processes (firm-queue start or flask firm worker) so you scale web and worker capacity independently. See Workers & the supervisor.