Skip to content

FastAPI Quickstart

Protect a FastAPI route with Baldur. No Redis, no Docker, no environment variables. The in-memory fallback covers the whole first run.

Supports Python 3.11–3.13. Assumes you have built a FastAPI app before.

1. Install

pip install baldur-framework[fastapi]

2. Wire Baldur into the app

fastapi_lifespan calls baldur.init() on startup; BaldurMiddleware adds rate-limit, backpressure, and circuit-breaker pre-flight at the ASGI edge. The /demo route is protected by the marquee @baldur.protected facade:

"""Minimal FastAPI app wired with Baldur.

``fastapi_lifespan`` calls ``baldur.init()`` on startup; ``BaldurMiddleware``
adds rate-limit + backpressure + circuit-breaker pre-flight at the ASGI edge.
The ``/demo`` route is protected by the marquee ``@baldur.protected`` facade.
Zero infrastructure: in-memory fallback, no Redis, no env vars.

Run the server with ``uvicorn app:app --reload`` from this directory, then
``curl http://127.0.0.1:8000/demo``. See ``docs/getting-started/fastapi.md``.
"""

from __future__ import annotations

from fastapi import FastAPI

import baldur
from baldur.adapters.fastapi import BaldurMiddleware, fastapi_lifespan

app = FastAPI(lifespan=fastapi_lifespan)
app.add_middleware(BaldurMiddleware)


@app.get("/demo")
@baldur.protected("demo")
def demo() -> dict[str, str]:
    """Return a JSON payload through Baldur's resilience pipeline."""
    return {"status": "ok", "service": "demo"}

Baldur does not authenticate your routes

The middleware adds resilience (rate-limit, backpressure, circuit breaker), not authentication. Your app keeps owning endpoint auth (FastAPI Depends, your own dependencies). Baldur's own operational surface (the built-in admin server and Web Console) is access-controlled separately by a key you configure; see that guide for the role model. The Django adapter additionally registers Django permission groups for its own admin/API views; there is no equivalent host-app role system on FastAPI.

3. Run it

Start the server and call the route:

uvicorn app:app --reload
curl http://127.0.0.1:8000/demo
# {"status": "ok", "service": "demo"}

That's it. The response just travelled through a circuit breaker.

See Baldur's events

Baldur logs to stdout automatically. Raise the log level to watch circuit breaker and rate-limit events as you exercise the endpoint:

export BALDUR_LOG_LEVEL=INFO   # circuit opened/closed, rate-limit blocks, ...

Verify without a browser

The quickstart ships a smoke test that drives the route through FastAPI's in-process TestClient — no server, no infra:

pytest examples/quickstart_fastapi/test_smoke.py

Browse the full runnable app: examples/quickstart_fastapi/.

Going to production

The in-memory fallback is single-process only

The zero-config path uses Baldur's in-memory cache. It keeps state in a per-process store, so copying this quickstart into a multi-worker deployment (uvicorn --workers N, gunicorn -k uvicorn.workers...) does not degrade gracefully: idempotency keys, rate-limit counters, and circuit breaker state diverge silently per worker. That breaks correctness, not just scale. The in-memory store also grows unbounded. This is a hazard, not a tuning knob: give Baldur a shared backend before you run more than one worker.

Point Baldur at Redis so all workers share state. No code changes needed: set one environment variable before starting the server:

pip install baldur-framework[fastapi,redis]
export BALDUR_REDIS_URL=redis://localhost:6379/0

That is the only addition the production path needs over the quickstart path.