Skillversitas

Event Handlers

Lifespan events let your app run code once at startup (load models, warm caches, open pools) and once at shutdown (close connections, flush writers). In current FastAPI the recommended pattern is the lifespan context manager passed to the FastAPI constructor.

The lifespan Context Manager

import asyncio
from contextlib import asynccontextmanager
from fastapi import FastAPI

@asynccontextmanager
async def lifespan(app: FastAPI):
    # startup
    app.state.cache = {}
    app.state.cache["hits"] = 0
    yield  # the app serves requests between yield
    # shutdown
    print("closing resources, final cache hits:", app.state.cache["hits"])

app = FastAPI(lifespan=lifespan)

@app.get("/")
def root():
    app.state.cache["hits"] += 1
    return {"hits": app.state.cache["hits"]}

Code before yield runs once before the first request; code after runs once when the server shuts down (SIGINT, SIGTERM).

The Old on_event Approach

app = FastAPI()

@app.on_event("startup")
def on_start():
    print("starting")

@app.on_event("shutdown")
def on_stop():
    print("stopping")

This still works but is deprecated; prefer lifespan, which supports async setup/teardown in one place and plays well with dependency injection.

Typical Startup Work

  • Open database pools or create tables (Base.metadata.create_all(engine)).
  • Load large objects (models, word lists) into app.state for reuse.
  • Initialize external clients (S3, SMTP, Redis) and verify connectivity.

Typical Shutdown Work

  • Close engines, clients, and file handles.
  • Flush background writers (metrics, logs).
  • Cancel in-flight background tasks you started at startup.

Summary

Use the lifespan context manager to run startup code before the first request and shutdown code at process exit, storing shared resources on app.state. It replaces the deprecated on_event handlers and gives you a single, testable place for all application lifecycle logic.