Skip to content

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.

postgres.py
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)
]
  1. Define lifespan hooks as async context managers returning your state
  2. Define dependency injection functions using get_state
  3. Create type aliases with typing.Annotated and fastapi.Depends: these will be used in route handlers to access the state
app.py
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)!
    ...
  1. Create a Lifespan instance combining all hooks in your application
  2. 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
@contextlib.asynccontextmanager
async def postgres_lifespan() -> abc.AsyncIterator[PoolType]:
    async with psycopg_pool.AsyncConnectionPool(...) as pool:
        yield pool

app = fastapi.FastAPI(
    lifespan=Lifespan(postgres_lifespan, redis_lifespan)
)
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 Lifespan instance.

Example
def _inject_pool(context: LifespanMap) -> PoolType:
    # Type of pool is PoolType (not object)
    pool = context.get_state(postgres_lifespan)
    return pool

Pool = t.Annotated[PoolType, fastapi.Depends(_inject_pool)]

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.

  • _ClassAsyncContextManager is a typing.Protocol that describes context managers that the Lifespan accepts.
  • FunctionLifespanHook describes the functions that Lifespan accepts.
  • ClassLifespanHook describes the callable returning a context manager that Lifespan accepts.
  • LifespanHook is the union of FunctionLifespanHook and ClassLifespanHook.
  • TypedLifespanHook is a generic version of LifespanHook that 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.