Starlette

middleware

Starlette is the ASGI toolkit under FastAPI: routing, middleware, websockets, background tasks and a test client. No validation layer, no dependency injection - FastAPI supplies those on top.

Its governance, ownership and the 1.0 milestone are covered on Python Web Frameworks. This page is what bites when you use it.

Checked against Starlette 1.9.0.

BaseHTTPMiddleware breaks contextvars

The most expensive gotcha here, because the symptom appears somewhere other than the cause.

Middleware is evaluated top to bottom, wrapped by ServerErrorMiddleware outermost and ExceptionMiddleware innermost. Printing the built stack confirms it:

ServerErrorMiddleware -> <your middleware, in declaration order> -> ExceptionMiddleware -> Router

That part is predictable. What is not: “Using BaseHTTPMiddleware will prevent changes to contextvars.ContextVars from propagating upwards.”

Reproduced with an endpoint doing cv.set("set-in-endpoint") and middleware reading cv.get() after the call:

stackwhat the outer middleware reads
one pure ASGI middlewareset-in-endpoint
pure ASGI middleware above a BaseHTTPMiddlewareunset

So a ContextVar set inside an endpoint is not readable from middleware sitting above a BaseHTTPMiddleware. The damage is not local: one convenient middleware silently disables correlation IDs, request-scoped tenancy or structured-logging context for every middleware above it. A pure ASGI middleware declared after it, so sitting below it, still reads set-in-endpoint. The docs word the same warning the other way round (“positioned earlier in the middleware stack, it will disrupt contextvars propagation for any subsequent Pure ASGI Middleware”); the run above is what happens. Nothing raises, the values simply come back at their defaults.

The documented answer is to write pure ASGI middleware instead. BaseHTTPMiddleware is the comfortable subclass; it is also the one with the caveat.

Request-parsing hardening, and what pinning costs

Most of the 1.3.x and 1.4.x work has been denial-of-service hardening on request parsing: max_fields and max_part_size enforcement in FormParser, absolute-path rejection in StaticFiles, suffix-range clamping in FileResponse, and GZip offloading.

That is the argument against pinning Starlette low to keep an old FastAPI: the fixes are in the layer that touches untrusted input first. FastAPI depends on starlette>=0.46.0 with no upper bound, so it accepts the 1.x line.

TestClient

Importing the test client with plain httpx installed emits, on 1.6.0:

StarletteDeprecationWarning: Using `httpx` with `starlette.testclient` is deprecated;
install `httpx2` instead.

That is the visible edge of a larger shift: httpx stalled and was superseded by pydantic/httpx2 (2.15.1 at the time of writing). Installing httpx2 silences the warning. See Python Web Frameworks.

Notes

  • Ships py.typed. It does not do type-hint-driven validation or dependency injection; that is FastAPI’s layer.

  • encode/uvicorn moved to Kludex/uvicorn alongside Starlette, and both are active. What stayed at encode did not: databases, orm, typesystem and broadcaster are archived.

  • https://starlette.dev/

Related

Keyboard shortcuts

/Open search
CtrlKOpen search (Cmd+K on macOS)
?Show this cheat sheet
Ctrl/Show this cheat sheet (Cmd+/ on macOS)
EscClose search or this cheat sheet

Turns off the single-key shortcuts (/, ?). Ctrl/Cmd+K and Ctrl/Cmd+/ stay.