Files
python-doc/Docs/Libs/FastAPI/15-Life-Span.md
2026-05-13 17:06:09 +03:30

6.9 KiB
Raw Blame History

FastAPI Application Lifespan, Startup, and Shutdown Events

Overview

FastAPI allows you to execute code when the application starts and when it shuts down. This is useful for initializing and cleaning up shared resources such as database connections, cache clients, machine learning models, message queues, or background services.

The modern recommended approach is to use the lifespan parameter with an async context manager. The older @app.on_event() method is still available, but FastAPI marks it as deprecated in favor of lifespan handlers. ([fastapi.tiangolo.com][1])


1. Deprecated Method: @app.on_event

Older FastAPI applications often use @app.on_event("startup") and @app.on_event("shutdown").

from fastapi import FastAPI

app = FastAPI()


@app.on_event("startup")
def on_startup():
    print("App is loading")


@app.on_event("shutdown")
def on_shutdown():
    print("App is shutting down")

Explanation

@app.on_event("startup")
def on_startup():
    print("App is loading")

This function runs once when the application starts.

@app.on_event("shutdown")
def on_shutdown():
    print("App is shutting down")

This function runs once when the application is shutting down.

Important Note

FastAPI documentation recommends using the lifespan parameter instead of @app.on_event(). Also, when a lifespan handler is provided, FastAPI does not call the old startup and shutdown event handlers. You should use one approach consistently, not both. ([fastapi.tiangolo.com][1])


2. Recommended Method: lifespan

The modern approach is to define one lifecycle function using asynccontextmanager.

from contextlib import asynccontextmanager
from fastapi import FastAPI


@asynccontextmanager
async def lifespan(app: FastAPI):
    print("App is loading")

    yield

    print("App is shutting down")


app = FastAPI(lifespan=lifespan)

3. How Lifespan Works

The lifespan function has two main sections:

@asynccontextmanager
async def lifespan(app: FastAPI):
    # Startup logic
    print("App is loading")

    yield

    # Shutdown logic
    print("App is shutting down")

Code Before yield

This runs when the application starts.

Use this section for:

Database connection setup
Cache connection setup
Loading configuration
Initializing shared services
Starting background clients

Code After yield

This runs when the application shuts down.

Use this section for:

Closing database connections
Closing cache clients
Flushing logs
Releasing resources
Stopping background services

FastAPI passes the lifespan context manager into the application and executes the code before yield on startup and after yield on shutdown. ([fastapi.tiangolo.com][1])


4. Example Application

Create or update main.py with the following content:

from contextlib import asynccontextmanager
from fastapi import FastAPI


@asynccontextmanager
async def lifespan(app: FastAPI):
    print("App is loading")

    yield

    print("App is shutting down")


app = FastAPI(lifespan=lifespan)


@app.get("/")
def root():
    return {"message": "API is working"}

5. Running the Application

Start the service using uvicorn:

uvicorn main:app --reload

When the application starts, the terminal prints:

App is loading

When you stop the application, for example with Ctrl + C, the terminal prints:

App is shutting down

6. Example: Database Initialization Pattern

In real applications, lifespan is commonly used to initialize and close database connections.

from contextlib import asynccontextmanager
from fastapi import FastAPI


async def connect_to_db():
    # Replace this with a real database connection
    return "database-connection"


@asynccontextmanager
async def lifespan(app: FastAPI):
    app.state.db = await connect_to_db()
    print("Database connected")

    yield

    # Replace this with real cleanup logic
    print("Database disconnected")


app = FastAPI(lifespan=lifespan)


@app.get("/")
def root():
    return {"message": "API is working"}

7. Using app.state

app.state is useful for storing shared application-level resources.

Example:

app.state.db = await connect_to_db()

Later, this resource can be accessed from the application.

Common resources stored in app.state include:

Database clients
Redis clients
HTTP clients
Configuration objects
Service clients
Loaded models

8. Better Database Cleanup Example

If the database client has a close() method, close it after yield.

from contextlib import asynccontextmanager
from fastapi import FastAPI


@asynccontextmanager
async def lifespan(app: FastAPI):
    app.state.db = await connect_to_db()
    print("Database connected")

    yield

    await app.state.db.close()
    print("Database disconnected")


app = FastAPI(lifespan=lifespan)

This ensures that the application does not leave open connections after shutdown.


9. Why Lifespan Is Better

The lifespan approach is better for modern FastAPI applications because it keeps startup and shutdown logic in one place.

It helps with:

Centralized lifecycle management
Cleaner async resource handling
Better application structure
Easier testing
More predictable production behavior
Cleaner resource cleanup

10. Complete Recommended Version

from contextlib import asynccontextmanager
from fastapi import FastAPI


@asynccontextmanager
async def lifespan(app: FastAPI):
    print("Application startup started")

    # Initialize shared resources here
    app.state.service_status = "ready"

    print("Application startup completed")

    yield

    print("Application shutdown started")

    # Clean up shared resources here
    app.state.service_status = "stopped"

    print("Application shutdown completed")


app = FastAPI(lifespan=lifespan)


@app.get("/")
def root():
    return {"message": "API is working"}


@app.get("/health")
def health_check():
    return {
        "status": "healthy",
        "service": app.state.service_status
    }

11. Testing the Application

Run the app:

uvicorn main:app --reload

Open:

http://localhost:8000/

Expected response:

{
  "message": "API is working"
}

Open:

http://localhost:8000/health

Expected response:

{
  "status": "healthy",
  "service": "ready"
}

12. Best Practices

Use lifespan for new FastAPI applications.

Avoid using @app.on_event() in new code because it is deprecated.

Do not mix lifespan with startup and shutdown event decorators.

Use app.state for shared application resources.

Close database connections, cache clients, HTTP clients, and background services during shutdown.

Keep startup logic lightweight.

Avoid using print() in production.

Use structured logging instead of printing to the console.

Do not use --reload in production.