Source code for alchemiq.fastapi.lifespan
"""FastAPI lifespan factory for managing the alchemiq engine lifecycle."""
from __future__ import annotations
from collections.abc import AsyncIterator, Callable
from contextlib import AbstractAsyncContextManager, asynccontextmanager
from typing import Any
from alchemiq.runtime.engine import configure, dispose
from alchemiq.runtime.engine import create_all as create_all_tables
[docs]
def lifespan(
dsn: str, *, create_all: bool = False, **engine_kwargs: Any
) -> Callable[[Any], AbstractAsyncContextManager[None]]:
"""Build a FastAPI lifespan that owns the alchemiq engine lifecycle.
Calls :func:`.configure` on startup and :func:`.dispose` on shutdown.
Pass the result directly to ``FastAPI(lifespan=...)``.
E.g.::
from fastapi import FastAPI
from alchemiq.fastapi import lifespan
app = FastAPI(lifespan=lifespan("postgresql+asyncpg://user:pw@host/db"))
# create tables on startup (dev/test only):
app = FastAPI(lifespan=lifespan(dsn, create_all=True))
:param dsn: SQLAlchemy async database URL
(e.g. ``"postgresql+asyncpg://user:pw@host/db"``).
:param create_all: when ``True``, run ``CREATE TABLE IF NOT EXISTS`` for all
registered models before the app starts accepting requests.
:param engine_kwargs: extra keyword arguments forwarded to
``create_async_engine`` (e.g. ``pool_size=10``).
:return: an async context-manager factory compatible with
``FastAPI(lifespan=...)``.
.. seealso:: :func:`.configure`, :func:`.dispose` - the underlying engine helpers.
"""
@asynccontextmanager
async def _cm(app: Any) -> AsyncIterator[None]:
configure(dsn, **engine_kwargs)
if create_all:
await create_all_tables()
try:
yield
finally:
await dispose()
return _cm