Idempotency keys¶
The middleware that replays the first response to a retried request.
In fastapi_alcazaba.idempotency.
In the guide: Idempotency keys. In an app: the example.
fastapi_alcazaba.idempotency.IdempotencyMiddleware
¶
IdempotencyMiddleware(app: ASGIApp, *, cache: Cache | None = None, methods: Collection[str] = ('POST', 'PATCH'), expire: int = 86400, identifier: Identifier | None = None, max_body_size: int = 1048576, max_response_size: int = 1048576, lock_ttl: float = 60.0, fail_open: bool = False)
ASGI middleware that makes requests with an Idempotency-Key header run once.
app.add_middleware(IdempotencyMiddleware)
Requests without the header go through untouched (require_idempotency_key makes it
mandatory on a route). The stored responses go through the cache's signing or encryption:
use an encryption_key when they carry personal data. They're kept without Set-Cookie.
A retry gets the stored response before the route's dependencies run: authentication included. With the default identifier, whoever has a copy of the request (its token, its key) gets the response again until it expires, even after the token is revoked; an identifier that checks the credentials (async, returning the user's id, raising a 401 otherwise) closes that.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cache
|
Cache | None
|
where responses and locks are kept; the app's ( |
None
|
methods
|
Collection[str]
|
the methods it applies to; PUT and DELETE are idempotent already. |
('POST', 'PATCH')
|
expire
|
int
|
seconds a response is kept for retries. |
86400
|
identifier
|
Identifier | None
|
who the client is ( |
None
|
max_body_size
|
int
|
larger request bodies get a 413: they're read whole to fingerprint them. |
1048576
|
max_response_size
|
int
|
larger responses aren't kept (a retry would run again). |
1048576
|
lock_ttl
|
float
|
seconds the lock outlives a process that dies while running the request. |
60.0
|
fail_open
|
bool
|
when the backend fails, run requests without idempotency instead of answering 503. Off by default: running a payment twice is worse than refusing it. |
False
|
fastapi_alcazaba.idempotency.require_idempotency_key
¶
require_idempotency_key(request: Request) -> str
FastAPI dependency for routes that must not run without an Idempotency-Key:
@app.post("/payments", dependencies=[Depends(require_idempotency_key)])
A request without one gets a 400; it returns the key.
fastapi_alcazaba.idempotency.authorization
¶
authorization(connection: HTTPConnection) -> str
The default identifier: the Authorization header (hashed before it's stored), so that
one user never gets another's response, whatever key they send.