Lifespan¶
The lifespan module provides composable FastAPI lifespan management with type-safe dependency injection.
Overview¶
FastAPI accepts only one lifespan callable, but applications often need
to manage multiple independent resources—database pools, cache clients,
HTTP sessions—each with its own setup and teardown logic. The Lifespan
class composes any number of async context managers (hooks) into a single
lifespan, storing each hook's yielded resource so that dependency
injection functions can retrieve it with full type preservation.
Basic Usage¶
import contextlib
import typing
import fastapi
from collections import abc
from imbi_common import lifespan
type PoolType = ... # your connection pool type
@contextlib.asynccontextmanager
async def db_hook() -> abc.AsyncIterator[PoolType]:
pool = PoolType(...)
await pool.open()
try:
yield pool
finally:
await pool.close()
async def _inject_pool(
context: lifespan.InjectLifespan,
) -> PoolType:
return context.get_state(db_hook)
DbPool = typing.Annotated[PoolType, fastapi.Depends(_inject_pool)]
app = fastapi.FastAPI(lifespan=lifespan.Lifespan(db_hook))
@app.get('/items')
async def list_items(*, pool: DbPool) -> list[dict]:
...
See the Lifespan and Dependency Injection guide for a full walkthrough of the pattern.
API Reference¶
Type Aliases¶
Classes¶
Lifespan ¶
Bases: dict[LifespanHook, object | None]
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.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
*hooks
|
LifespanHook
|
Variable number of lifespan hooks to combine. Each hook is an async context manager that yields a resource. |
()
|
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 InjectLifespan: Type alias for dependency injection
Initialize Lifespan with the given hooks.
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. |
()
|
Source code in src/imbi_common/lifespan.py
__call__ ¶
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 (unused, required by FastAPI lifespan protocol). |
required |
Returns:
| Type | Description |
|---|---|
AbstractAsyncContextManager[dict[str, Lifespan]]
|
contextlib.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
Source code in src/imbi_common/lifespan.py
get_state ¶
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.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hook
|
TypedLifespanHook[T]
|
The lifespan hook whose resource to retrieve. Must have been passed to the Lifespan constructor. |
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: InjectLifespan) -> PoolType:
# Type of pool is PoolType (not object)
pool = context.get_state(postgres_lifespan)
return pool