FastAPI lifespan events

Kevin Tewouda
2 min readMar 7, 2023

Simplify logic for startup and shutdown events

Photo by Michał Mancewicz on Unsplash

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 😉

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Kevin Tewouda
Kevin Tewouda

Written by Kevin Tewouda

Déserteur camerounais résidant désormais en France. Passionné de programmation, sport, de cinéma et mangas. J’écris en français et en anglais dû à mes origines.

Responses (3)

Write a response

Excelent! This is what I was looking for.. usage of the app state within the lifespan event.

Hi there,
Recently I've started using (<FastAPI>+<TortoiseORM>+<fastapi-users>+<fastapi-users-tortoiseORM>) tech-stack (the latter is a package to support using <fastapi-users> with <tortoiseORM>).
The issue is when I setup any of [Startup/Lifespan]…

Great addition to FastAPI, thanks for the post!