Skip to content

FastAPI Easy Versioning

PyPI - Python Version PyPI - Downloads GitHub Release GitHub Repo stars Test results Coverage Status Documentation

A library for building versioned APIs with FastAPI. Each API version is a separate FastAPI sub-application mounted under a common application. The library automatically inherits endpoints from older versions into newer ones and rebuilds each version's OpenAPI schema, so every version's /docs always shows its complete, current set of endpoints.

Features

  • Endpoint inheritance between versions: declare an endpoint once and it is available in all subsequent versions.
  • Precise availability range control with the until parameter.
  • Redefining an endpoint in a newer version shadows the inherited one — both at runtime and in the OpenAPI schema.
  • HTTP and WebSocket endpoints are supported.
  • Versioning metadata (origin, until) is readable right inside the endpoint.
  • Several independent versioned APIs within one application.
  • The only runtime dependency is fastapi (versions from 0.95 up to the latest are supported).

Installation

pip install fastapi-easy-versioning

PyPI

Quick Start

Versioning is built from two pieces that only work together:

  • VersioningMiddleware — added to the application that mounts the versions;
  • versioning() — a dependency factory that marks an endpoint as versioned.
from fastapi import FastAPI, Depends
from fastapi_easy_versioning import VersioningMiddleware, versioning

app = FastAPI()
app_v1 = FastAPI(api_version=1)
app_v2 = FastAPI(api_version=2)

app.mount("/v1", app_v1)
app.mount("/v2", app_v2)
app.add_middleware(VersioningMiddleware)

@app_v1.get('/only-v1', dependencies=[Depends(versioning(until=1))])
def only_v1() -> str:
    return "Available only in version v1"

@app_v1.get('/all-versions', dependencies=[Depends(versioning())])
def all_versions() -> str:
    return "Available in all versions starting from v1"

@app_v2.get('/from-v2', dependencies=[Depends(versioning())])
def from_v2() -> str:
    return "Available starting from v2 and in all future versions"
graph TD
    A[Availability by version]
    A --> B[v1]
    A --> C[v2]

    B --> B1["/only-v1 ✓"]
    B --> B2["/all-versions ✓"]
    B --> B3["/from-v2 ✗"]

    C --> C1["/only-v1 ✗"]
    C --> C2["/all-versions ✓"]
    C --> C3["/from-v2 ✓"]

    style B1 fill:#90EE90
    style B2 fill:#90EE90
    style B3 fill:#FFB6C1

    style C1 fill:#FFB6C1
    style C2 fill:#90EE90
    style C3 fill:#90EE90

The result:

  • /v1/only-v1 responds while /v2/only-v1 returns 404 — the endpoint is limited by until=1.
  • /v1/all-versions and /v2/all-versions both respond — the endpoint is declared in v1 and inherited into v2.
  • /v2/from-v2 responds while /v1/from-v2 returns 404 — the endpoint only appeared in v2.
  • /v1/docs and /v2/docs show exactly the endpoints available in the corresponding version.

How It Works

  1. Each version is a FastAPI(api_version=N) sub-application mounted under a common application: app.mount("/v1", app_v1). api_version must be an integer.
  2. On its first ASGI event (server startup or the first request) VersioningMiddleware builds the inheritance once: it copies the marked endpoints from older versions into newer ones and rebuilds each version's OpenAPI schema.
  3. Only endpoints with the versioning() dependency are inherited. An endpoint without it stays only in the version where it is declared.
  4. Every inheriting version receives its own copy of the route — changes in one version do not affect the others.

Documentation Sections