FastAPI · Application Lifecycle
FastAPI Lifespan: Set Up Shared Resources Before Requests Arrive
Give shared resources an explicit startup and shutdown boundary, then test both ends of the application lifecycle.

Some resources belong to an application lifecycle: a loaded model or a connection pool can serve many requests. Creating them during every request repeats setup work; creating them at import time also affects tools that merely import the module.
Put ownership in lifespan
FastAPI accepts an async context manager through FastAPI(lifespan=lifespan). Work before yield prepares the application before it receives requests. Cleanup after yield releases resources when the application shuts down.
For a shared resource, the control flow can look like this illustrative outline:
@asynccontextmanager
async def lifespan(app):
resource = await open_resource()
try:
yield
finally:
await resource.close()
Import asynccontextmanager from contextlib. The open_resource() and close() calls represent your resource library, not FastAPI APIs. Connect the resource to your handlers using an explicit application-level access pattern.
Keep request ownership separate
A shared pool and an individual request's database session have different lifetimes. Decide which object is shared and which must be acquired and released for each request before choosing where cleanup belongs.
Test both ends
Use with TestClient(app) as client so lifespan runs during a test. Check that setup has completed inside the block, make a request, and check cleanup after leaving it. A response assertion alone does not demonstrate shutdown behavior.
Use one lifecycle mechanism
FastAPI recommends lifespan for startup and shutdown. When supplying lifespan, do not expect the alternative startup and shutdown event handlers to run alongside it.