FastAPI lifespan events
Simplify logic for startup and shutdown events
I want to talk about a slight improvement brought in FastAPI 0.93.0. Prior to this version, if we were going to do some initialization setup and cleanup at the shutdown of the application, we needed to define two functions. Here is a simple example of initializing a database connection using Tortoise ORM.
from fastapi import FastAPI
from tortoise import Tortoise
app = FastAPI()
@app.on_event('startup')
async def initialize_db_connection():
await Tortoise.init(
db_url='sqlite://db.sqlite3',
modules={'models': ['app.models']}
)
await Tortoise.generate_schemas()
@app.on_event('shutdown')
async def close_db_connection():
await Tortoise.close_connections()
This works well, but often the logic in the startup and shutdown functions is linked and we lost the global picture when we split it into two functions. Also, these functions don’t accept the application object where you can store some state.
There is a new way to do the startup/shutdown logic leveraging the ASGI lifespan protocol. Now you can use a single function like the following:
from contextlib import asynccontextmanager
from fastapi import FastAPI
from tortoise import Tortoise
@asynccontextmanager
async def lifespan(application: FastAPI):
await Tortoise.init(
db_url='sqlite://db.sqlite3',
modules={'models': ['app.models']}
)
yield
await Tortoise.close_connections()
app = FastAPI(lifespan=lifespan)
@app.get("/")
async def root():
return {"message": "Hello World"}
Notes:
- The function must be written as an async context manager.
- There is only one function we can write for lifespan events.
- You can now access the application object and eventually store some state in it.
For the last case, here is a simple example of how you can use it.
import secrets
from contextlib import asynccontextmanager
from fastapi import FastAPI, Request
@asynccontextmanager
async def lifespan(application: FastAPI):
application.state.super_secret = secrets.token_hex(16)
yield
app = FastAPI(lifespan=lifespan)
@app.get("/")
async def root(request: Request):
return {"secret": request.app.state.super_secret}
In your HTTP function handler, you can access the request object, and the request object has access to the application object, so you can access its state. 😁
This is all for this article hope you find this new feature useful in your next project.
If you like my article and want to continue learning with me, don’t hesitate to follow me here and subscribe to my newsletter on substack 😉