FastAPI integration¶
The [fastapi] extra wires alchemiq into FastAPI: an automatic CRUD router
factory, request-scoped dependency providers, a global exception handler, and a
lifespan helper that manages the database engine.
pip install "alchemiq[fastapi,postgres]"
Exception handler¶
Register alchemiq’s global exception handler once, at app startup, so every
PersistenceError converts to a JSON response automatically:
from fastapi import FastAPI
from alchemiq.fastapi import install_exception_handlers
app = FastAPI()
install_exception_handlers(app)
The mapping is:
Exception |
HTTP status |
|---|---|
|
404 |
|
409 |
|
409 |
|
500 |
any other |
500 |
InvalidCursorError is not caught by this handler - the list endpoint in
crud_router converts it directly to an HTTPException(400).
Automatic CRUD router¶
crud_router builds a fully-wired APIRouter from a model or repository.
By default all five standard endpoints are mounted:
Method |
Path |
Description |
|---|---|---|
|
|
Paginated list |
|
|
Single-row read (404 if missing) |
|
|
Create, returns 201 |
|
|
Partial update (unset fields are untouched) |
|
|
Soft or hard delete, returns 204 |
Minimal example¶
from fastapi import 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"))
crud_router accepts a bare model class, a Repository instance, or a
Repository subclass.
Parameters¶
Parameter |
Type |
Default |
Description |
|---|---|---|---|
|
model or |
(required) |
Model class or repository to expose |
|
|
|
URL prefix for all routes, e.g. |
|
|
|
OpenAPI tags applied to every route |
|
Pydantic model |
auto-derived |
Response schema |
|
Pydantic model |
auto-derived |
Create request body schema |
|
Pydantic model |
auto-derived |
Partial-update request body schema |
|
|
all five |
Subset of |
|
|
|
List endpoint pagination style |
|
|
|
FastAPI dependencies applied to every route |
|
|
|
Allowlist of field names the |
|
type |
|
Python type used to parse the |
Subset of endpoints with cursor pagination¶
from fastapi import Depends
app.include_router(
crud_router(
Repository(Article),
prefix="/articles",
tags=["articles"],
dependencies=[Depends(require_token)],
endpoints={"list", "read", "create"},
pagination="cursor",
)
)
pagination="cursor" switches the list endpoint to keyset pagination with
after / before query parameters. The response envelope becomes a
CursorPage (items, next_cursor, prev_cursor, has_next,
has_prev) instead of the default offset Page (items, total,
page, size, pages, has_next, has_prev).
Schema auto-derivation¶
Pydantic schemas for request bodies and responses are derived automatically from the model’s field metadata. Pass explicit schemas to override:
app.include_router(
crud_router(
User,
prefix="/users",
read_schema=UserOut,
create_schema=UserCreate,
update_schema=UserPatch,
)
)
Dependency injection¶
alchemiq.fastapi re-exports the same providers available in
alchemiq.faststream. Use them as Depends targets to get a session,
repository, or unit of work scoped to each HTTP request:
from fastapi import Depends, FastAPI
from alchemiq import Repository
from alchemiq.fastapi import repository, unit_of_work, db_session
app = FastAPI()
@app.get("/users/{user_id}")
async def get_user(
user_id: int,
users: Repository = Depends(repository(User)),
):
return await users.get(id=user_id)
@app.post("/orders")
async def create_order(
body: OrderCreate,
uow=Depends(unit_of_work),
):
async with uow:
order = await Repository(Order).create(**body.model_dump())
return order
App lifespan¶
Use lifespan from alchemiq.fastapi to wire the SQLAlchemy engine to the
FastAPI startup/shutdown lifecycle without writing boilerplate:
from fastapi import FastAPI
from alchemiq.fastapi import lifespan
import alchemiq
app = FastAPI(lifespan=lifespan("postgresql+asyncpg://user:pass@localhost/mydb"))
The helper calls alchemiq.configure(dsn) on startup and disposes the engine
on shutdown.