FastAPI integration¶
FastAPI integration for alchemiq. Requires the [fastapi] extra.
- alchemiq.fastapi.crud_router(repository, *, prefix='', tags=None, read_schema=None, create_schema=None, update_schema=None, endpoints=None, pagination='offset', dependencies=None, filter_allow=None, id_type=<class 'int'>)[source]¶
Build a FastAPI
APIRouterwith standard CRUD endpoints for an alchemiq model.repositorymay be aRepositoryinstance, subclass, or a bare model class. By default all five endpoints are mounted:GET /- paginated list (offset or cursor)GET /{id}- single read (404 if missing)POST /- create, returns 201 with the created resourcePATCH /{id}- partial update (only supplied fields are changed)DELETE /{id}- soft or hard delete, returns 204 with no body
Pass
endpoints={"list", "read"}to mount a subset.pagination="cursor"switches the list endpoint to keyset pagination (after/beforequery params) returning aCursorPageenvelope instead of the default offsetPageenvelope.Pydantic schemas are auto-derived from the model’s field metadata via
read_schema(),create_schema(), andupdate_schema(); pass explicit schemas to override.E.g.:
from fastapi import Depends, FastAPI from alchemiq import Repository from alchemiq.fastapi import crud_router, install_exception_handlers app = FastAPI() install_exception_handlers(app) app.include_router(crud_router(User, prefix="/users")) # Subset of endpoints with cursor pagination and auth dependency: app.include_router( crud_router( Repository(Article), prefix="/articles", tags=["articles"], dependencies=[Depends(require_token)], endpoints={"list", "read", "create"}, pagination="cursor", ) )
- Parameters:
repository (Any) – a
Repositoryinstance, subclass, or bare model class.prefix (str) – URL prefix for all routes (e.g.
"/items").tags (list[str | Enum] | None) – OpenAPI tags applied to every route.
read_schema (type[BaseModel] | None) – override the auto-derived read (response) Pydantic schema.
create_schema (type[BaseModel] | None) – override the auto-derived create (request body) schema.
update_schema (type[BaseModel] | None) – override the auto-derived partial-update schema.
endpoints (set[str] | None) – subset of
{"list", "read", "create", "update", "delete"}to mount; defaults to all five.pagination (Literal['offset', 'cursor']) –
"offset"(default) or"cursor"for the list endpoint.dependencies (Sequence[Any] | None) – FastAPI dependencies applied to every route.
filter_allow (set[str] | None) – allowlist of field names the
qquery param may filter on.id_type (type) – Python type used to parse the
{id}path parameter (defaultint).
- Returns:
a configured
APIRouterready to pass toapp.include_router().- Raises:
ConfigError – if
endpointscontains an unknown name.- Return type:
APIRouter
See also
install_exception_handlers()- wirePersistenceError-> HTTP.
- alchemiq.fastapi.health_router(*, prefix='/health', timeout=5.0, include_liveness=True)[source]¶
Build an
APIRouterwithGET /readyand optionallyGET /liveendpoints.GET /readycallscheck_health()and returns 200 when all components are healthy or 503 when any component is degraded - suitable for a KubernetesreadinessProbe.GET /livealways returns 200 with{"status": "alive"}and has no backend dependencies - suitable for a KuberneteslivenessProbe.E.g.:
from fastapi import FastAPI from alchemiq.fastapi import health_router app = FastAPI() app.include_router(health_router()) # custom prefix: app.include_router(health_router(prefix="/probe")) # readiness only - omit the /live route: app.include_router(health_router(include_liveness=False))
- Parameters:
prefix (str) – URL prefix for the health routes (default
"/health").timeout (float) – seconds passed to
check_health()for backend probes (default5.0).include_liveness (bool) – when
False, theGET /liveroute is omitted.
- Returns:
a configured
APIRouterready to pass toapp.include_router().- Return type:
APIRouter
See also
check_health()- the underlying health probe implementation.
- alchemiq.fastapi.lifespan(dsn, *, create_all=False, **engine_kwargs)[source]¶
Build a FastAPI lifespan that owns the alchemiq engine lifecycle.
Calls
configure()on startup anddispose()on shutdown. Pass the result directly toFastAPI(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))
- Parameters:
dsn (str) – SQLAlchemy async database URL (e.g.
"postgresql+asyncpg://user:pw@host/db").create_all (bool) – when
True, runCREATE TABLE IF NOT EXISTSfor all registered models before the app starts accepting requests.engine_kwargs (Any) – extra keyword arguments forwarded to
create_async_engine(e.g.pool_size=10).
- Returns:
an async context-manager factory compatible with
FastAPI(lifespan=...).- Return type:
Callable[[Any], AbstractAsyncContextManager[None]]
See also
configure(),dispose()- the underlying engine helpers.
- alchemiq.fastapi.repository(repo_or_model)[source]¶
Return a zero-arg callable that yields the resolved repository (use as a DI default).
- Parameters:
repo_or_model (Any)
- Return type:
Callable[[], Repository]
- async alchemiq.fastapi.unit_of_work()[source]¶
Yield a
UnitOfWorkscoped to the request (FastAPI/FastStream dependency).- Return type:
- async alchemiq.fastapi.db_session()[source]¶
Yield a read-only
AsyncSessionscoped to the request (FastAPI/FastStream dependency).- Return type:
- alchemiq.fastapi.install_exception_handlers(app)[source]¶
Register a global handler that converts
PersistenceErrorto JSON responses.Attaches a single
app.add_exception_handlerforPersistenceError. Each subtype is mapped to an HTTP status bystatus_for(); the response body is{"detail": "<exception message>"}.E.g.:
from fastapi import FastAPI from alchemiq.fastapi import install_exception_handlers, crud_router app = FastAPI() install_exception_handlers(app) app.include_router(crud_router(User, prefix="/rows"))
- Parameters:
app (FastAPI) – the
FastAPIapplication instance.- Return type:
None
Note
InvalidCursorErroris not caught by this handler. The list endpoint incrud_router()raises it as anHTTPException(400)directly.See also
status_for()- the exception->status mapping.
- alchemiq.fastapi.http_exception_for(exc)[source]¶
Convert a
PersistenceErrorto anHTTPExceptionwith the mapped status code.- Parameters:
exc (PersistenceError) – a
PersistenceError(or subclass) instance.- Returns:
an
HTTPExceptionwhosestatus_codeis determined bystatus_for()and whosedetailis the string representation of exc.- Return type:
HTTPException
- alchemiq.fastapi.status_for(exc)[source]¶
Return the HTTP status code for a
PersistenceError.The mapping is:
NotFoundError-> 404MultipleResultsFound-> 409ConcurrentModificationError-> 409RelationNotLoaded-> 500any other
PersistenceErrorsubtype -> 500 (fallback)
InvalidCursorErroris not in this map -crud_router()raises it directly as anHTTPExceptionwith status 400.- Parameters:
exc (PersistenceError) – a
PersistenceError(or subclass) instance.- Returns:
the HTTP status code integer.
- Return type:
- alchemiq.fastapi.read_schema(model)[source]¶
Return the cached Pydantic read schema for model (all serialisable fields).
The schema is derived from the model’s field metadata and cached on the class.
crud_router()uses this automatically; call it directly to build a response schema for a custom endpoint.
- alchemiq.fastapi.create_schema(model)[source]¶
Return a cached Pydantic schema for creating model instances.
Required fields are required; nullable fields are optional with a
Nonedefault. PK, server-managed timestamps (created_at/updated_at), anddeleted_at(on soft-delete models) are excluded so clients cannot supply them.
- alchemiq.fastapi.update_schema(model)[source]¶
Return a cached Pydantic schema for partial updates of model instances.
All writable fields are optional with a
Nonedefault.crud_router()passes the body throughmodel_dump(exclude_unset=True)so only the fields the client actually supplied are applied.
- alchemiq.fastapi.page_schema(read_model)[source]¶
Return a cached offset-page envelope schema wrapping read_model.
Produced automatically by
crud_router()whenpagination="offset"(default). The envelope fields areitems,total,page,size,pages,has_next, andhas_prev.- Parameters:
read_model (type[BaseModel]) – a Pydantic
BaseModel(typically fromread_schema()).- Returns:
a cached
BaseModelsubclass named{ReadModel}Page.- Return type:
- alchemiq.fastapi.cursor_page_schema(read_model)[source]¶
Return a cached cursor-page envelope schema wrapping read_model.
Produced automatically by
crud_router()whenpagination="cursor". The envelope fields areitems,next_cursor,prev_cursor,has_next, andhas_prev.- Parameters:
read_model (type[BaseModel]) – a Pydantic
BaseModel(typically fromread_schema()).- Returns:
a cached
BaseModelsubclass named{ReadModel}CursorPage.- Return type: