Endpoint deprecation management for FastAPI made easy
Project description
FastAPI Deprecation
FastAPI Deprecation helps you manage the lifecycle of your API endpoints using standard HTTP headers (Deprecation, Sunset, Link) and automated blocking logic. It allows you to gracefully warn clients about upcoming deprecations and automatically shut down endpoints when they reach their sunset date.
Features
- Standard Compliance: Fully implements RFC 9745 and RFC 8594 with support for multiple link relations (
rel="alternate",rel="successor-version", etc.). - Decorator-based & Middleware: Simple
@deprecateddecorator for path operations, andDeprecationMiddlewarefor globally deprecating prefixes or intercepting 404s for sunset endpoints. - Automated Blocking: Automatically returns
410 Goneor308 Permanent Redirect(configurable) after thesunset_date. - Dynamic OpenAPI Integration: Dynamically modifies the Swagger UI/ReDoc to mark active deprecations and announces future upcoming deprecations without requiring application restarts.
- Client-Side Caching: Optionally injects
Cache-Control: max-ageto ensure warning responses aren't cached beyond the sunset date. - Cache Invalidation: Inject
Cache-TagorSurrogate-Keyfor instant edge caching CDN (Cloudflare/Fastly) validation. - Real-Time Streams: First-class support for deprecating WebSockets (with initial handshake HTTP headers and Graceful Closure) and Server-Sent Events (SSE) using injected stream closure events.
- Extended Features:
- Brownouts (Scheduled & Chaos): Schedule temporary shutdowns or configure probabilistic failure rates to simulate future removal and progressively force client migrations.
- Telemetry: Track usage of deprecated endpoints.
- Rate Limiting: Hook into your favorite rate limiting library (e.g.,
slowapi) to dynamically throttle legacy traffic.
Installation
pip install fastapi-deprecation
# or with uv
uv add fastapi-deprecation
Documentation
To run the documentation locally:
uv run zensical serve
Quick Start
from fastapi import FastAPI
from fastapi_deprecation import deprecated, auto_deprecate_openapi
app = FastAPI()
@app.get("/old-endpoint")
@deprecated(
deprecation_date="2024-01-01",
sunset_date="2025-01-01",
alternative="/new-endpoint",
detail="This endpoint is old and tired."
)
async def old():
return {"message": "Enjoy it while it lasts!"}
# Don't forget to update the schema at the end!
auto_deprecate_openapi(app)
Example Application
For a comprehensive demonstration of all features (Middleware, Router-level deprecation, mounted sub-apps, custom responses, and brownouts), check out the Showcase Application included in the repository:
uv run python examples/showcase.py
Open http://localhost:8000/docs to see the API lifecycle in action.
How It Works
-
Warning Phase (Before Sunset):
- Requests return
200 OK. - Response headers include:
Deprecation: @1704067200(Unix timestamp ofdeprecation_date)Sunset: Wed, 01 Jan 2025 00:00:00 GMTLink: </new-endpoint>; rel="alternative"
- Requests return
-
Blocking Phase (After Sunset):
- Requests return
410 Gone(or301 Moved Permanentlyifalternativeis set, customizable viaalternative_status). - The
detailmessage is returned in the response body.
- Requests return
Advanced Usage
1. Brownouts (Scheduled & Chaos)
You can simulate future shutdowns by scheduling "brownouts" — temporary periods where the endpoint returns 410 Gone (301 if alternative is present) or custom responses. This forces clients to notice the deprecation before the final sunset.
You can configure hardcoded datetime windows, or utilize Chaos Engineering probabilities to randomly fail requests.
@deprecated(
deprecation_date="2025-01-01",
sunset_date="2025-12-31",
# 1. Scheduled: Fail during these exact windows
brownouts=[
("2025-11-01T09:00:00Z", "2025-11-01T10:00:00Z"),
],
# 2. Static Chaos: 5% of all traffic fails constantly
# Note: mutually exclusive with progressive_brownout
brownout_probability=0.05,
detail="Service is temporarily unavailable due to scheduled brownout."
)
async def my_endpoint(): ...
@deprecated(
deprecation_date="2025-01-01",
sunset_date="2025-12-31",
# 3. Progressive Chaos: Failure rate scales dynamically from 0% on Jan 1st to 100% on Dec 31st
progressive_brownout=True,
detail="Service is progressively degrading and will be removed."
)
async def progressive_endpoint(): ...
2. Telemetry & Logging
Track usage of deprecated endpoints using a global callback. This is useful for monitoring which clients are still using old APIs.
import logging
from typing import Any
from fastapi import Request, Response
from fastapi_deprecation import set_deprecation_callback, DeprecationConfig
logger = logging.getLogger("deprecation")
def log_usage(request: Request, response: Response, dep: DeprecationConfig):
logger.warning(
f" ⚠ Deprecated endpoint {request.url} accessed. "
f"Deprecation date: {dep.deprecation_date}"
)
set_deprecation_callback(log_usage)
Advanced Analytics: Looking for cross-worker aggregated counters, Redis synchronization, or Prometheus text exposition scraping? See the Universal Metrics & Telemetry Documentation.
3. Deprecating Entire Routers
To deprecate a whole group of endpoints, use DeprecationDependency on the APIRouter.
from fastapi import APIRouter, Depends
from fastapi_deprecation import DeprecationDependency
router = APIRouter(
dependencies=[Depends(DeprecationDependency(deprecation_date="2024-01-01"))]
)
@router.get("/sub-route")
async def sub(): ...
4. Recursive OpenAPI Support
When using auto_deprecate_openapi(app), it automatically traverses potentially mounted sub-applications (app.mount(...)) and marks their routes as deprecated if configured.
root_app.mount("/v1", v1_app)
# This will update OpenAPI for both root_app AND v1_app
auto_deprecate_openapi(root_app)
5. Future Deprecation & Caching
You can announce a future deprecation date. The Deprecation header will still be sent, allowing clients to prepare.
You can also inject Cache-Control headers so clients don't mistakenly cache warning responses past the sunset date, or inject Cache-Tag / Surrogate-Key headers to instantly purge CDN edge caches.
@deprecated(
deprecation_date="2030-01-01",
sunset_date="2031-01-01",
inject_cache_control=True,
cache_tag="api-v1-deprecation-group"
)
async def future_proof(): ...
6. Custom Response Models & Multiple Links
Customize the HTTP 410/308 response payload dynamically using response, and provide extensive contextual documentation via multiple RFC 8594 Link relations.
from starlette.responses import JSONResponse
custom_error = JSONResponse(
status_code=410,
content={"message": "This endpoint is permanently removed. Use v2."}
)
@deprecated(
sunset_date="2024-01-01",
response=custom_error,
links={
"alternate": "https://api.example.com/v2/items",
"latest-version": "https://api.example.com/v3/items"
}
)
async def custom_sunset(): ...
7. Real-Time Streams (WebSockets & SSE)
You can deprecate WebSockets and Server-Sent Events identically to standard paths.
WebSockets: The @deprecated decorator automatically hooks into the handshake phase to emit Deprecation and Sunset headers when you call await websocket.accept(). If the sunset date has passed, it natively raises a WebSocketException to cleanly deny the upgrade.
from fastapi import WebSocket
@app.websocket("/ws")
@deprecated(sunset_date="2024-01-01")
async def ws_endpoint(websocket: WebSocket):
# Deprecation headers are automatically attached during accept!
await websocket.accept()
Server-Sent Events (SSE): When returning a StreamingResponse with media_type="text/event-stream", the @deprecated decorator will completely automatically wrap your stream. When a configured sunset_date or brownout inevitably triggers during a long-lived open connection, the wrapper seamlessly injects a final event: sunset directly into the stream and terminates the loop gracefully, notifying the client that real-time signals are ending. (Note: if using Global Middleware or Router-level Dependencies, you must wrap the stream manually using deprecated_sse_generator as the decorator intercept is required for auto-wrapping).
from starlette.responses import StreamingResponse
@app.get("/stream")
@deprecated(sunset_date="2025-01-01")
async def sse_endpoint():
# The stream is automatically intercepted, wrapped, and safely terminated!
return StreamingResponse(your_generator(), media_type="text/event-stream")
8. Global Middleware
Deprecate entire prefixes at the ASGI level, intercepting 404 Not Found errors for removed routes and correctly returning 410 Gone with deprecation metadata.
from fastapi_deprecation import DeprecationMiddleware, DeprecationConfig
app.add_middleware(
DeprecationMiddleware,
deprecations={
"/api/v1": DeprecationConfig(sunset_date="2025-01-01")
}
)
See the Documentation for full details on API reference and advanced configuration.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fastapi_deprecation-0.5.0.tar.gz.
File metadata
- Download URL: fastapi_deprecation-0.5.0.tar.gz
- Upload date:
- Size: 98.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4643036bd154798ce9b6c6fd9c8697769bea5e195d38652e580cc52a483d8b6d
|
|
| MD5 |
598fdc5f771417341bd893509babd01d
|
|
| BLAKE2b-256 |
57ad20ae1c0b940fff43c5b9cbff326bc8ab64b0b9617414d4b9dac4a1564b01
|
Provenance
The following attestation bundles were made for fastapi_deprecation-0.5.0.tar.gz:
Publisher:
publish.yml on fractalvision/fastapi-deprecation
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fastapi_deprecation-0.5.0.tar.gz -
Subject digest:
4643036bd154798ce9b6c6fd9c8697769bea5e195d38652e580cc52a483d8b6d - Sigstore transparency entry: 1032328897
- Sigstore integration time:
-
Permalink:
fractalvision/fastapi-deprecation@68902b00dd8db416617cc0c45bcb6db99f7fe875 -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/fractalvision
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@68902b00dd8db416617cc0c45bcb6db99f7fe875 -
Trigger Event:
push
-
Statement type:
File details
Details for the file fastapi_deprecation-0.5.0-py3-none-any.whl.
File metadata
- Download URL: fastapi_deprecation-0.5.0-py3-none-any.whl
- Upload date:
- Size: 28.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
01d804b3320ce721560f9527fe2ffacdd4c03958bf88063cd1db85ba95dc812b
|
|
| MD5 |
b3a1ff0aa222b4ed804ca7b0abce15ad
|
|
| BLAKE2b-256 |
567cc68568ed616941c5f87c95cac3e7e3eb014a92b0b94098cd0e609451647b
|
Provenance
The following attestation bundles were made for fastapi_deprecation-0.5.0-py3-none-any.whl:
Publisher:
publish.yml on fractalvision/fastapi-deprecation
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fastapi_deprecation-0.5.0-py3-none-any.whl -
Subject digest:
01d804b3320ce721560f9527fe2ffacdd4c03958bf88063cd1db85ba95dc812b - Sigstore transparency entry: 1032329035
- Sigstore integration time:
-
Permalink:
fractalvision/fastapi-deprecation@68902b00dd8db416617cc0c45bcb6db99f7fe875 -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/fractalvision
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@68902b00dd8db416617cc0c45bcb6db99f7fe875 -
Trigger Event:
push
-
Statement type: