Alcazaba
Secure, maintained tools for FastAPI
Response cache, rate limits, sessions, CSRF, webhooks, a task queue and more, in one package.
Documentation: https://nonodev96.github.io/fastapi-alcazaba/
Source code: https://github.com/nonodev96/fastapi-alcazaba
Alcazaba (fastapi-alcazaba) gathers the pieces most FastAPI apps end up writing themselves: a
response cache, rate limits, authorization, sessions, CSRF protection, webhooks, a task queue and
more, built with security in mind.
The key features are:
- Secure by default: users never get each other's cached responses, nothing that opens a door (keys, codes, tokens) is stored in clear, and whatever could leak data is opt-in.
- Shared by every process: rate limits, locks, sessions, revoked tokens, feature flags and maintenance mode live in Redis, Valkey, memcached or DynamoDB, so that every process and container sees them at once.
- Resilient: every call to the backend has a timeout and a circuit breaker. A backend that fails doesn't take the app down.
- The FastAPI way: dependencies, decorators and middlewares that show in the OpenAPI docs, with types your editor completes.
- Standards: problem details (RFC 9457),
Cache-Status(RFC 9211), the IETF'sRateLimitheaders, Standard Webhooks, Casbin, Cedar and Open Policy Agent policies, and OpenTelemetry. - Tested hard: against real servers on Python 3.11 to 3.14, with fuzzing and mutation testing.
- Many tools, one package: a response cache, rate and concurrency limits, idempotency keys, authentication strategies, API keys and tokens, passkeys and a second factor (TOTP), authorization, accounting and quotas, sessions and CSRF, an audit trail, upload checks, webhooks, scheduled jobs, a task queue, circuit breakers, maintenance mode and feature flags. See them all.
Requirements¶
Python 3.11 or later, FastAPI 0.115 or later and Pydantic v2. Everything else is optional: each backend and integration comes with an extra.
Installation¶
$ pip install fastapi-alcazaba
That's enough for an app in a single process, with everything kept in memory.
Example¶
Create it¶
Create a file main.py with:
from collections.abc import AsyncIterator
from contextlib import asynccontextmanager
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from fastapi_alcazaba import FastAPICache
from fastapi_alcazaba.backends.inmemory import InMemoryBackend
from fastapi_alcazaba.response_cache import cache
@asynccontextmanager
async def lifespan(_: FastAPI) -> AsyncIterator[None]:
FastAPICache.init(InMemoryBackend(), prefix="shop")
yield
app = FastAPI(lifespan=lifespan)
class Product(BaseModel):
name: str
price: float
PRODUCTS = {1: Product(name="Olive oil", price=9.5)}
@app.get("/products/{product_id}")
@cache(expire=60)
async def read_product(product_id: int) -> Product:
if product_id not in PRODUCTS:
raise HTTPException(404, "No such product")
return PRODUCTS[product_id]
Run it¶
Run it with fastapi dev, which comes with pip install "fastapi[standard]" (or
uvicorn main:app --reload):
$ fastapi dev main.py
⚡️ Starting FastAPI in development mode
🌐 Server started at http://127.0.0.1:8000
Documentation at http://127.0.0.1:8000/docs
Check it¶
Ask for the product twice:
$ curl -i http://127.0.0.1:8000/products/1
HTTP/1.1 200 OK
cache-status: Alcazaba; fwd=miss; stored
cache-control: max-age=60
{"name":"Olive oil","price":9.5}
$ curl -i http://127.0.0.1:8000/products/1
HTTP/1.1 200 OK
cache-status: Alcazaba; hit; ttl=60
cache-control: max-age=60
{"name":"Olive oil","price":9.5}
The first request ran read_product; the second one came from the cache, which keeps the
response for 60 seconds. Requests with an Authorization or Cookie header get entries of
their own: users never see each other's responses.
Interactive API docs¶
Open http://127.0.0.1:8000/docs: the endpoint is there as FastAPI documents it, with its parameters and response model.
Example upgrade¶
The Tutorial - User Guide takes this app further, a step at a time: five requests a minute for each client, then prices that only the staff may change, as a Casbin policy says:
@app.put("/products/{product_id}", dependencies=[Depends(authz)])
async def update_product(product_id: int, product: Product) -> Product:
if product_id not in PRODUCTS:
raise HTTPException(404, "No such product")
PRODUCTS[product_id] = product
await FastAPICache.invalidate_tags(f"product:{product_id}")
return product
Recap¶
In summary, you add Alcazaba's tools the way FastAPI already works: a decorator caches an endpoint, a dependency limits it or checks who may call it, and the lifespan sets up where their state lives. The app keeps its OpenAPI schema and its types, and moving from memory to Redis changes one line.
For a more complete example, with more tools, see the Tutorial - User Guide.
Optional dependencies¶
Each backend and integration needs a library of its own, which an extra installs:
| Extra | Installs | For |
|---|---|---|
redis |
redis-py | RedisBackend |
valkey |
valkey-py | ValkeyBackend |
glide |
Valkey GLIDE | GlideBackend |
memcache |
aiomcache | MemcachedBackend |
dynamodb |
aiobotocore | DynamoBackend |
otel |
the OpenTelemetry API | metrics and traces |
crypto |
cryptography | encrypted entries |
zstd |
backports.zstd, before Python 3.14 | zstd compression |
casbin |
PyCasbin | the enforcers of FastAPIAuthz |
cedar |
cedarpy | CedarEnforcer |
opa |
httpx2 | OPAEnforcer |
auth-strategy-jwt |
PyJWT, with cryptography | JWTStrategy |
auth-strategy-oauth2 |
httpx2 | OAuth2Strategy |
auth-strategy-oidc |
httpx2, PyJWT | OIDCStrategy |
auth-strategy-saml |
signxml | SAMLStrategy |
auth-strategy-unkey |
httpx2 | UnkeyStrategy |
auth-strategy-http-bearer |
nothing more | HTTPBearerStrategy |
all |
all of the above |
$ pip install "fastapi-alcazaba[redis,casbin]"
License¶
This project is licensed under the terms of the Apache License 2.0. Its credits, and where its name comes from, are in About.