Skip to main content

FastAPI Backports

License: MIT Python 3.8+ FastAPI 0.115.0+

A Python library that provides backports for FastAPI features, allowing you to use newer FastAPI functionality on older versions. This includes both unreleased fixes and experimental features based on FastAPI community discussions that might be included in future FastAPI releases.

Features

This library includes backports for:

🔄 Multiple Query Models Support

  • Issue: Support multiple Query() in a single endpoint
  • Description: Enables using multiple Pydantic models as query parameters in a single FastAPI endpoint
  • Benefits: Better organization of query parameters and improved API design

What this fixes:

import fastapi_backports.apply  # noqa: F401

from dataclasses import dataclass
from fastapi import Query, FastAPI
from typing import Annotated, Any


@dataclass
class FilterParams:
    category: str | None = None
    min_price: float | None = None


@dataclass
class PaginationParams:
    page: int = 1
    size: int = 10


app = FastAPI()


# ❌ Without backport: This would not work as expected
@app.get("/items")
async def get_items(
    filters: Annotated[FilterParams, Query()],
    pagination: Annotated[PaginationParams, Query()],
) -> dict[str, Any]:
    return {"filters": filters, "pagination": pagination}

# ✅ With backport: This works perfectly!
# Query parameters are properly flattened and validated
# Example: /items?category=electronics&min_price=10&page=2&size=20

🏷️ PEP 695 Type Alias Support

What this fixes:

import fastapi_backports.apply  # noqa: F401

from typing import Annotated
from fastapi import Depends, FastAPI


async def get_current_user_id() -> int:
    return 123


# PEP 695 style type alias (Python 3.12+)
type UserId = Annotated[int, Depends(get_current_user_id)]

app = FastAPI()


# ❌ Without backport: FastAPI doesn't recognize the type alias properly
# ✅ With backport: Type aliases work seamlessly in dependencies
@app.get("/users/{user_id}")
async def get_user(user_id: UserId):
    return {"user_id": user_id}

🕵️ Add middleware parameter to APIRouter

  • Issue: Add middleware parameter to APIRouter
  • Description: Enables adding middleware directly to APIRouter instances and individual routes
  • Benefits: Better middleware organization and route-specific middleware support

What this fixes:

import fastapi_backports.apply  # noqa: F401

from typing import Any

from fastapi_backports import FastAPI, APIRouter
from fastapi import Request
from fastapi.middleware import Middleware
from starlette.types import ASGIApp, Receive, Scope, Send


def add_header_middleware(_app: ASGIApp, header_name: str, header_value: str) -> ASGIApp:
    async def _middleware(scope: Scope, receive: Receive, send: Send) -> None:
        async def send_wrapper(message):
            if message["type"] == "http.response.start":
                headers = message.setdefault("headers", [])
                headers.append((header_name.encode(), header_value.encode()))
            await send(message)

        await _app(scope, receive, send_wrapper)

    return _middleware


app = FastAPI()

# ❌ Without backport: APIRouter doesn't accept middleware parameter
# ✅ With backport: You can now add middleware to routers
router = APIRouter(
    prefix="/api/v1",
    middleware=[
        Middleware(add_header_middleware, header_name="X-Custom-Header", header_value="Value")
    ]
)


@router.get("/items")
async def get_items() -> dict[str, Any]:
    return {"items": ["item1", "item2"]}


# Middleware will be applied to all routes in this router
app.include_router(router)

🔍 QUERY HTTP method support

What this fixes:

import fastapi_backports.apply  # noqa: F401

from typing import Any
from fastapi import FastAPI
from pydantic import BaseModel

class SearchQuery(BaseModel):
    filters: dict[str, Any]
    sort_by: str | None = None
    limit: int = 100

app = FastAPI()

# ❌ Without backport: QUERY method not supported
# ✅ With backport: You can now use the QUERY HTTP method
@app.query("/search")
async def search_items(query: SearchQuery) -> dict[str, Any]:
    return {
        "results": ["item1", "item2", "item3"],
        "filters_applied": query.filters,
        "sorted_by": query.sort_by,
        "total": 150
    }

# QUERY method allows complex search parameters in request body
# while maintaining safe, idempotent semantics
# Usage: QUERY /search with JSON body containing SearchQuery data

📝 Postponed Type Annotations Support

  • Issue: Can't use Annotated with ForwardRef
  • Description: Fixes handling of postponed type annotations (PEP 563) and forward references in FastAPI
  • Benefits: Enables proper type checking and dependency injection with complex type hierarchies

What this fixes:

from __future__ import annotations

import fastapi_backports.apply  # noqa: F401

from dataclasses import dataclass
from typing import Annotated

from fastapi import Depends, FastAPI

app = FastAPI()


# Forward reference to a class defined later
def get_potato() -> Potato:
    return Potato(color='red', size=10)


# ❌ Without backport: ForwardRef causes issues with dependency injection
# ✅ With backport: Forward references work seamlessly
@app.get('/')
async def read_root(potato: Annotated[Potato, Depends(get_potato)]) -> Potato:
    return potato


@dataclass
class Potato:
    color: str
    size: int

🔄 Multiple Lifespans Support

  • Issue: Support multiple Lifespan in FastAPI app
  • Description: Enables adding multiple lifespan context managers to FastAPI applications and routers
  • Benefits: Better organization of startup/shutdown logic and modular lifespan management

What this fixes:

from contextlib import asynccontextmanager
from typing import AsyncIterator, Any

import fastapi_backports.apply  # noqa: F401

from fastapi import FastAPI

app = FastAPI()

# ❌ Without backport: Only one lifespan context is supported
# ✅ With backport: You can add multiple lifespan contexts

@app.add_lifespan
@asynccontextmanager
async def database_lifespan(_app: FastAPI) -> AsyncIterator[dict[str, Any]]:
    print("Starting database connection")
    yield {"db": "connection"}
    print("Closing database connection")


@app.add_lifespan
@asynccontextmanager
async def cache_lifespan(_app: FastAPI) -> AsyncIterator[dict[str, Any]]:
    print("Starting cache")
    yield {"cache": "connection"}
    print("Stopping cache")


@app.get("/")
async def read_root():
    return {"message": "Hello World"}

# Both lifespans will be executed in order during startup/shutdown

Installation

pip install fastapi-backports

Usage

Basic Usage

Import the backport module before using FastAPI:

import fastapi_backports.apply  # noqa: F401
from fastapi import FastAPI, APIRouter

app = FastAPI()


@app.get("/")
async def root():
    return {"message": "Hello World"}

The backports are automatically applied when you import fastapi_backports.apply.

[!NOTE] The # noqa: F401 comment is needed to prevent linters from complaining about an "unused import". While the import appears unused, it actually applies the backports through side effects when imported.

Manual Backport Control

For more control over when backports are applied:

import fastapi_backports
from fastapi import FastAPI

# ⚠️ IMPORTANT: Apply backports BEFORE creating FastAPI app or routes
fastapi_backports.backport()

# Now create your app
app = FastAPI()


@app.get("/")
async def root():
    return {"message": "Hello World"}

[!IMPORTANT] Always call fastapi_backports.backport() before creating your FastAPI application instance or defining any routes. The backports modify FastAPI's internal behavior and must be applied before FastAPI processes your route definitions.

Free Backport Control

Both import fastapi_backports.apply and fastapi_backports.backport() will directly apply all Backports in fastapi_backports. If you want to apply a specific Backport, do as follows:

import fastapi_backports
from fastapi import FastAPI

# ⚠️ IMPORTANT: Apply backports BEFORE creating FastAPI app or routes
fastapi_backports.backport(
    backports=[
        fastapi_backports.MultipleQueryModelsBackporter,
        fastapi_backports.TypeAliasTypeBackporter,
    ]
)

# Now create your app
app = FastAPI()


@app.get("/")
async def root():
    return {"message": "Hello World"}

Metadata

Release files for fastapi-backports 0.1.5

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fastapi-backports 0.1.5
File Size Uploaded Provenance
fastapi_backports-0.1.5.tar.gz 105.7 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for fastapi-backports 0.1.5
File Interpreter ABI Platform
fastapi_backports-0.1.5-py3-none-any.whl Python 3 none any Details

Total release size: 125.1 kB

Release files / fastapi_backports-0.1.5.tar.gz

Download URL fastapi_backports-0.1.5.tar.gz
Size 105.7 kB
Tags Source
SHA-256 checksum
How to use checksums
6293bae736cbdf039d4d32780658b79b31882c7ca6410219916da98d1b87af0d
BLAKE2b-256 checksum
How to use checksums
7203782ec4fbc5c3fed23796751436ffd8a9fa02912c5b2caf2820f12b7692b7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.4 {"installer":{"name":"uv","version":"0.12.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / fastapi_backports-0.1.5-py3-none-any.whl

Download URL fastapi_backports-0.1.5-py3-none-any.whl
Size 19.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
78025a14f6a6e22213706737bdade9a01449520ac198ddcec6213688cfde9a9d
BLAKE2b-256 checksum
How to use checksums
2157521103e3f70e2b7816ef80a8c07d1b1465805b88c61cda9268d8f3f9eede
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.4 {"installer":{"name":"uv","version":"0.12.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for fastapi-backports 0.1.5

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page