FastAPI: From Basics to Production · Dependency Injection in Depth

Lifespan Events and Shared Clients

Create and close app-wide resources like HTTP clients and pools.

Use the lifespan context manager to create shared resources at startup (database engine, httpx.AsyncClient, Redis pool, ML models) and close them at shutdown. Store them on app.state or module-level objects and expose them through dependencies. Reusing a single async HTTP client enables connection pooling.

from contextlib import asynccontextmanager
import httpx
from fastapi import FastAPI, Request


@asynccontextmanager
async def lifespan(app: FastAPI):
    app.state.http = httpx.AsyncClient(timeout=httpx.Timeout(5.0), limits=httpx.Limits(max_connections=100))
    yield
    await app.state.http.aclose()
    await engine.dispose()


app = FastAPI(lifespan=lifespan)


def get_http(request: Request) -> httpx.AsyncClient:
    return request.app.state.http


@app.get("/rates")
async def rates(http: Annotated[httpx.AsyncClient, Depends(get_http)]):
    resp = await http.get("https://api.example.com/rates")
    resp.raise_for_status()
    return resp.json()

Common pitfalls

  • Creating a new HTTP client per request (no connection reuse).
  • Using deprecated @app.on_event('startup') in new code.
  • No timeouts on outbound HTTP calls.

Key points

  • lifespan for startup/shutdown
  • Share pooled clients
  • Always set timeouts

Further reading: FastAPI: Lifespan events

Practice questions

Why reuse one httpx.AsyncClient instead of creating one per request?

Answer

A shared client keeps a connection pool, avoiding repeated TCP/TLS handshakes and reducing latency and resource use.

What replaces @app.on_event('startup')?

Answer

The lifespan async context manager passed to FastAPI(lifespan=...): code before yield runs at startup, after yield at shutdown.

Open in the app to mark lessons complete, take notes and track your progress.