fastapi-easy-versioning¶
Versioned APIs for FastAPI. One sub-application per version, automatic inheritance of endpoints from older versions into newer ones, and an up-to-date OpenAPI schema for every version.
Why¶
Once an API lives in several versions, routes have to be copied between them by hand: /v2 must serve everything /v1 served, minus what was deliberately dropped. The copies drift apart, /v1/docs and /v2/docs start lying, and "which versions is this endpoint even in?" becomes a question for git blame.
fastapi-easy-versioning makes the availability range a property of the endpoint: declare it once, mark it with a dependency, and it shows up in every later version by itself.
app_v1 = FastAPI()
app_v2 = FastAPI()
@app_v1.get("/items")
def items_v1() -> list[Item]: # (1)!
return get_items()
@app_v2.get("/items")
def items_v2() -> list[Item]:
return get_items()
- The same endpoint, duplicated by hand. Every new version adds another copy, and one day one of them will be left un-updated.
app = FastAPI()
app.add_middleware(VersioningMiddleware)
app.mount("/v1", app_v1)
app.mount("/v2", app_v2)
@app_v1.get("/items", dependencies=[Depends(versioning())]) # (1)!
def items() -> list[Item]:
return get_items()
- Declared once in v1 — and available in v2 and every future version. The
/v2/docsschema builds itself.
@app_v1.get("/legacy", dependencies=[Depends(versioning(until=2))]) # (1)!
def legacy() -> str:
return "gone after v2"
- Available in v1 and v2, absent from v3 — at runtime and in the schema alike. See the
untilsemantics.
Every version stays an ordinary FastAPI application: its own routing, its own /docs, its own dependency_overrides.
graph TD
A[Availability by version]
A --> B[v1]
A --> C[v2]
B --> B1["/only-v1 ✓"]:::available
B --> B2["/all-versions ✓"]:::available
B --> B3["/from-v2 ✗"]:::missing
C --> C1["/only-v1 ✗"]:::missing
C --> C2["/all-versions ✓"]:::available
C --> C3["/from-v2 ✓"]:::available
classDef available fill:#90EE90,stroke:#333,color:#1b1b1b
classDef missing fill:#FFB6C1,stroke:#333,color:#1b1b1b
Features¶
-
Inheritance between versions
An endpoint is declared once and lands in every later version on its own. Each version gets its own copy of the route.
-
An exact availability range
untilsets the last version an endpoint is available in. Without it — "through the latest version", including ones that do not exist yet. -
Redefinition shadows
An endpoint of your own on the same path in a newer version shadows the inherited one — at runtime and in OpenAPI.
-
An honest
/docsper version
After inheritance every version's OpenAPI schema is regenerated, so Swagger shows exactly what that version serves.
-
HTTP and WebSocket
APIRouteandAPIWebSocketRouteare versioned alike; the version metadata is readable right inside the endpoint. -
Several independent APIs
Public and private APIs in one application version separately — one middleware per aggregating application.
Install¶
Requirements
- Python 3.10+
- FastAPI ≥ 0.95 (
0.137.0and0.137.1are excluded — see Limitations) - Nothing else:
fastapiis the only runtime dependency
Next¶
- Quickstart — a working app in a minute.
- Guide — dependency, middleware, recipes, limitations.
- Examples — runnable apps from the repository.
- API Reference — signatures from the docstrings.