Hasina Razafintsalama

Hasina RAZAFINTSALAMA

← Back to Blog
Backend

FastAPI in 2026: What Changed in Routing and Validation

FastAPI 0.139 refines how routers match requests and tightens JSON validation by default. Here is what matters if you maintain a FastAPI service.

2026-06-27·6 min

FastAPI still has not hit 1.0, and that is by design,Sebastián Ramírez ships incremental improvements rather than a big-bang rewrite. Version 0.139, released July 1, 2026, is a good snapshot of where the framework is heading: more control over routing, stricter validation by default, and a smoother path for large codebases still on Pydantic v1.

Custom route matching with APIRouter.matches() and .handle()

APIRouter gained two new methods that let you customize how a router decides whether it should handle a request. A common use case: routing by a header instead of a URL prefix, useful for API versioning without cluttering every path with /v1, /v2.

python
from fastapi import APIRouter, Request

class HeaderVersionedRouter(APIRouter):
    def __init__(self, *args, version: str, **kwargs):
        super().__init__(*args, **kwargs)
        self.version = version

    def matches(self, request: Request) -> bool:
        return request.headers.get("X-API-Version") == self.version

router_v1 = HeaderVersionedRouter(version="1")
router_v2 = HeaderVersionedRouter(version="2")

Adding routes to an already-included router

Routes added to a router after it has been included in the app now show up correctly,previously they were copied at inclusion time and later additions were silently ignored. Sub-routers can also be included in a parent router before the sub-router itself has any routes, which saves memory in apps that build routers dynamically.

Stricter JSON validation by default

FastAPI now checks that incoming JSON requests carry a valid Content-Type header before attempting to parse the body. A request claiming to send JSON without the right header is rejected instead of silently misparsed,this closes a class of subtle client bugs. If you have clients that do not set the header correctly, you can opt out.

python
from fastapi import FastAPI

app = FastAPI()

@app.post("/webhooks/legacy-partner")
async def legacy_webhook(payload: dict):
    # Some partner integrations still send JSON without a Content-Type header.
    # Explicitly opt out of the new default instead of failing silently.
    ...

The opt-out is a per-route strict_content_type=False flag on the route decorator. Use it deliberately for legacy integrations, not as a blanket default,the stricter behavior catches real bugs.

Mixing Pydantic v1 and v2 in the same app

Large FastAPI codebases that never finished migrating off Pydantic v1 now have an official bridge: importing from pydantic.v1 lets v1 and v2 models coexist in the same application. This turns a risky big-bang migration into an incremental one, model by model.

python
from pydantic.v1 import BaseModel as LegacyModel  # old, not-yet-migrated models
from pydantic import BaseModel  # new endpoints use Pydantic v2 directly

class LegacyOrder(LegacyModel):
    id: int
    total: float

class Order(BaseModel):
    id: int
    total: float
    currency: str = "EUR"

Python 3.14 support

FastAPI dropped Python 3.8 from CI and added Python 3.14 support, while its internal syntax was upgraded to target Python 3.9+. If you are still pinned to Python 3.8, this is the release that forces the conversation about upgrading your runtime.

None of these changes are breaking by default,the stricter Content-Type check is the one to test for if your clients are loosely typed. Everything else is additive: better routing control, a migration bridge, and a wider supported Python range.

Need help with this topic? REST API Design

Discover this service