Composable FastAPI Lifespans¶
fastapi_patterns.lifespan
¶
FastAPI lifespan composition with type-safe dependency injection.
Problem
FastAPI accepts only one lifespan callable, but applications need multiple independent resources (database pools, Redis connections) with separate setup/teardown lifecycles.
Solution
The Lifespan class composes multiple async state providers into a single lifespan while preserving type information through dependency injection.
Use the following pattern to define state that is readily available in any request handler.
from fastapi_patterns import lifespan
@contextlib.asynccontextmanager
async def postgres_lifespan() -> abc.AsyncIterator[PoolType]: # (1)!
async with psycopg_pool.AsyncConnectionPool(...) as pool:
yield pool
async def _inject_pool(
context: lifespan.LifespanMap
) -> abc.AsyncIterator[PoolType]:
pool = context.get_state(postgres_lifespan) # (2)!
async with pool.connection() as conn:
yield conn
PostgresPool = t.Annotated[ # (3)!
PoolType, fastapi.Depends(_inject_pool)
]
- Define lifespan hooks as async context managers returning your state
- Define dependency injection functions using get_state
- Create type aliases with typing.Annotated and fastapi.Depends: these will be used in route handlers to access the state
import fastapi
from fastapi_patterns import lifespan
from my_package import postgres
app = fastapi.FastAPI(
lifespan=lifespan.Lifespan(postgres.postgres_lifespan), # (1)!
)
@app.get('/')
async def handler(*, pool: postgres.PostgresPool) -> None: # (2)!
...
- Create a Lifespan instance combining all hooks in your application
- Use type aliases from your provider in route handlers to access the state
Type Aliases:
| Name | Description |
|---|---|
LifespanMap |
Dependency injection for Lifespan instance. |
Classes:
| Name | Description |
|---|---|
Lifespan |
Compose multiple lifespan hooks into a single FastAPI lifespan. |
Functions:
| Name | Description |
|---|---|
get_lifespan |
Extract the Lifespan instance from the request state. |
LifespanMap = t.Annotated[Lifespan, fastapi.Depends(get_lifespan)]
¶
Dependency injection for Lifespan instance.
Mention this type in a parameter list to inject the Lifespan instance anywhere that FastAPI's dependency injection is available.
Lifespan(*hooks)
¶
Compose multiple lifespan hooks into a single FastAPI lifespan.
Manages multiple independent async context managers (lifespan hooks) and provides type-safe access to their yielded resources through dependency injection. Hooks are deduplicated (same hook only runs once) and cleaned up in LIFO order.
Example
See Also
- get_state: Retrieve resources from hooks with type preservation
- LifespanMap: Type alias for dependency injection
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*hooks
|
LifespanHook
|
Variable number of lifespan hooks to combine. Hooks are entered in the order provided and exited in LIFO order. Duplicate hooks are deduplicated automatically. |
()
|
Methods:
| Name | Description |
|---|---|
__call__ |
Make Lifespan callable as a FastAPI lifespan function. |
get_state |
Retrieve the resource yielded by a specific hook. |
__call__(app)
¶
Make Lifespan callable as a FastAPI lifespan function.
This method is called automatically by FastAPI during application startup. It enters all registered hooks, stores their yielded resources, and ensures proper cleanup on shutdown.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
app
|
FastAPI
|
The FastAPI application instance. |
required |
Returns:
| Type | Description |
|---|---|
AbstractAsyncContextManager[dict[str, Lifespan]]
|
An async context manager that yields a dictionary containing the Lifespan instance under the key 'lifespan_data'. |
Note
- Hooks are entered in the order provided to
__init__ - Duplicate hooks are detected and only executed once
- Resources are cleaned up in LIFO order (last-in-first-out)
- Uses AsyncExitStack to ensure proper cleanup even if hooks raise exceptions
get_state(hook)
¶
Retrieve the resource yielded by a specific hook.
This is a generic method that preserves type information.
If the hook yields a resource of type T, this method
returns T. Use this method to create dependency injection
functions for use with fastapi.Depends.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hook
|
TypedLifespanHook[T]
|
The lifespan hook whose resource to retrieve. Must have been passed to the initializer. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
T |
T
|
The resource yielded by the hook, with type preserved. |
Raises:
| Type | Description |
|---|---|
HTTPException
|
500 error if the hook was not
registered with this |
get_lifespan(request)
¶
Extract the Lifespan instance from the request state.
This is a FastAPI dependency function that retrieves the Lifespan instance from the request state.
Warning
You should be using LifespanMap instead!
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
request
|
Request
|
The current request object. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
Lifespan |
Lifespan
|
The Lifespan instance that was set up during application startup. |
Raises:
| Type | Description |
|---|---|
HTTPException
|
500 error if the lifespan was not initialized (missing lifespan parameter in FastAPI() constructor) or if request.state.lifespan_data is not accessible. |
Additional type machinery¶
There are a number of type aliases used in this module that you will see referenced in signatures. You shouldn't need to use them directly or worry about them too much, They are used to make the type system work for you and a part of what makes the small dependency injection helpers work so well.
_ClassAsyncContextManageris a typing.Protocol that describes context managers that the Lifespan accepts.FunctionLifespanHookdescribes the functions that Lifespan accepts.ClassLifespanHookdescribes the callable returning a context manager that Lifespan accepts.LifespanHookis the union ofFunctionLifespanHookandClassLifespanHook.TypedLifespanHookis a generic version ofLifespanHookthat participates in type inference in get_state
You shouldn't need to use these directly, but if you want to dig into them for some reason, at least you have some explanation of what they are there for.