Starlette
raw JSON → 1.0.0 verified Tue May 12 auth: no python install: verified quickstart: verified
Lightweight ASGI framework and toolkit. Foundation for FastAPI. Released version 1.0.0 on Mar 22, 2026 — first stable release after 8 years on ZeroVer (0.x). Now follows SemVer. 1.0 removed all features deprecated during the 0.x era. Directly used by the Python MCP SDK.
pip install starlette Warnings
breaking on_startup= and on_shutdown= parameters removed in 1.0. add_event_handler() also removed. All three were the primary patterns in older tutorials and LLM-generated code. ↓
fix Use lifespan context manager: @asynccontextmanager async def lifespan(app): yield — pass as app = Starlette(lifespan=lifespan).
breaking @app.route() and @app.websocket_route() decorators removed in 1.0. Were deprecated since 0.23.0 but widely used in tutorials for years. ↓
fix Use declarative routing: Starlette(routes=[Route('/', handler), WebSocketRoute('/ws', ws_handler)]).
breaking Jinja2Templates now raises ImportError at import time if jinja2 is not installed. Previously only raised when instantiating the class. ↓
fix pip install jinja2 or pip install 'starlette[full]'
breaking Jinja2Templates now enables autoescape by default. Templates that relied on unescaped output (e.g. rendering HTML strings) will now escape them. ↓
fix Mark trusted HTML strings with Markup() from markupsafe, or pass autoescape=False to Jinja2Templates() to restore old behavior.
breaking TestClient now uses httpx instead of requests (changed in 0.20.0). Test suites using requests-specific APIs on TestClient responses will break. ↓
fix pip install httpx. Use httpx response API — most methods are compatible. Use the bump-testclient migration tool for larger test suites.
breaking Python 3.8 and 3.9 support dropped. Starlette 1.0 requires Python >=3.10. ↓
fix Upgrade Python to 3.10+. Pin starlette<1.0 to remain on Python 3.8/3.9.
gotcha Starlette is typically a transitive dependency via FastAPI — do not pin starlette version directly. FastAPI manages its own compatible Starlette range. Pinning starlette independently can cause conflicts. ↓
fix Let FastAPI manage the starlette version. Only pin starlette directly if using it standalone without FastAPI.
Install
pip install "starlette[full]" Install compatibility verified last tested: 2026-05-12
python os / libc variant status wheel install import disk
3.10 alpine (musl) full - - 0.00s 26.2M
3.10 alpine (musl) starlette - - 0.00s 20.7M
3.10 slim (glibc) full - - 0.00s 27M
3.10 slim (glibc) starlette - - 0.00s 21M
3.11 alpine (musl) full - - 0.00s 29.1M
3.11 alpine (musl) starlette - - 0.00s 22.7M
3.11 slim (glibc) full - - 0.00s 30M
3.11 slim (glibc) starlette - - 0.00s 23M
3.12 alpine (musl) full - - 0.00s 20.8M
3.12 alpine (musl) starlette - - 0.00s 14.5M
3.12 slim (glibc) full - - 0.00s 22M
3.12 slim (glibc) starlette - - 0.00s 15M
3.13 alpine (musl) full - - 0.00s 20.1M
3.13 alpine (musl) starlette - - 0.00s 13.8M
3.13 slim (glibc) full - - 0.00s 21M
3.13 slim (glibc) starlette - - 0.00s 14M
3.9 alpine (musl) full - - 0.00s 25.5M
3.9 alpine (musl) starlette - - 0.00s 20.0M
3.9 slim (glibc) full - - 0.00s 27M
3.9 slim (glibc) starlette - - 0.00s 21M
Imports
- lifespan wrong
app = Starlette( on_startup=[startup_handler], on_shutdown=[shutdown_handler] ) # on_startup/on_shutdown removed in 1.0correctfrom contextlib import asynccontextmanager from starlette.applications import Starlette @asynccontextmanager async def lifespan(app): # startup yield # shutdown app = Starlette(lifespan=lifespan) - Route (declarative) wrong
@app.route('/') async def homepage(request): ... # @app.route() decorator removed in 1.0correctfrom starlette.applications import Starlette from starlette.routing import Route async def homepage(request): ... app = Starlette(routes=[Route('/', homepage)])
Quickstart verified last tested: 2026-04-23
from contextlib import asynccontextmanager
from starlette.applications import Starlette
from starlette.requests import Request
from starlette.responses import JSONResponse
from starlette.routing import Route
@asynccontextmanager
async def lifespan(app):
# startup logic
yield
# shutdown logic
async def homepage(request: Request):
return JSONResponse({'hello': 'world'})
app = Starlette(
routes=[Route('/', homepage)],
lifespan=lifespan,
)
# Run: uvicorn main:app