Skip to content

FastAPI

from contextlib import asynccontextmanager
from fastapi import FastAPI
from z4j_fastapi import Z4JAgent
@asynccontextmanager
async def lifespan(app: FastAPI):
agent = Z4JAgent(
brain_url=os.environ["Z4J_BRAIN_URL"],
token=os.environ["Z4J_TOKEN"],
project_id=os.environ.get("Z4J_PROJECT_ID", "default"),
agent_name=os.environ.get("HOSTNAME", "fastapi"),
)
async with agent:
app.state.z4j = agent
yield
app = FastAPI(lifespan=lifespan)

FastAPI’s @app.on_event("startup") is deprecated. The lifespan context manager is the current idiom; it cleanly runs the agent for the full server lifetime.

arq and taskiq workers are separate processes from the FastAPI app. They also need an agent. Pattern:

# arq worker
from arq import ArqRedis
from z4j_fastapi import Z4JAgent
async def on_startup(ctx):
ctx["z4j"] = Z4JAgent(...)
await ctx["z4j"].__aenter__()
async def on_shutdown(ctx):
await ctx["z4j"].__aexit__(None, None, None)
class WorkerSettings:
functions = [...]
on_startup = on_startup
on_shutdown = on_shutdown

Same shape for taskiq (startup / shutdown hooks).

During uvicorn --reload, agents reconnect on every save. The brain tolerates this - rapid reconnects don’t create duplicate agents because tokens are unique.

Each worker is a separate agent process. Set agent_name with $PID if you want per-worker visibility.

z4j is not an authentication provider for your FastAPI app. The agent only authenticates to the brain. Your app’s own auth is untouched.

python -m z4j_fastapi doctor runs the same probes the agent runtime does (buffer dir writable, brain DNS / TCP / TLS, WebSocket upgrade) using the Z4J_* env vars your service is configured with.

Terminal window
# Always run as the same user the service runs under.
sudo -u app /srv/app/venv/bin/python -m z4j_fastapi doctor
# Skip the WS round-trip when the brain is intentionally offline:
python -m z4j_fastapi doctor --no-websocket
# Machine-readable for scripting:
python -m z4j_fastapi doctor --json

Exits 0 on all-green, 1 on any failure. Catches uvicorn-under-service-user silent startup failures, NAT / firewall / cert issues, and wrong-token / wrong-project_id problems with a specific failure reason. See service-user deployments.

First, run python -m z4j_fastapi doctor — it surfaces the most common failures with a specific reason.

  • PermissionError: ... /nonexistent/.z4j under uvicorn - the service user has an unwritable $HOME. Upgrade to z4j-bare 1.0.6+ (auto-relocates buffer to $TMPDIR/z4j-{uid}) and see service-user deployments.
  • Agent only registers under uvicorn, not under arq/taskiq workers - workers are separate processes and need their own Z4JAgent(...) boot in the worker’s on_startup hook.

Same as Django/Flask - see Django.