Skip to main content

ADR-006: FastAPI, SQLAlchemy 2 async, Alembic, uv, Ruff, mypy

Status: accepted

Context

A template's stack is judged twice: is it what people expect in 2026, and does it stay out of the inner rings?

Decision

ConcernChoiceWhyWhy not the alternatives
HTTPFastAPIde-facto standard for typed async Python APIs; Depends is enough DI; OpenAPI for freeLitestar is excellent but smaller; Django REST couples the ORM to everything
Validation at the edgePydantic v2comes with FastAPI; fast; extra="forbid" catches client typosused only in http/ and bootstrap/settings.py
PersistenceSQLAlchemy 2.0 async + separate row modelsmature, typed Mapped[], any SQL database; the domain stays framework-freeSQLModel merges Pydantic + table into the entity - exactly the coupling we avoid; raw asyncpg means writing an ORM
MigrationsAlembicthe SQLAlchemy migration tool; render_as_batch covers SQLite-
Default DBSQLite (aiosqlite)zero setup; the point is to run in two minutesPostgreSQL is one env var and a compose file away, and CI tests it
Package manageruvfast, lockfile, single tool for venv + deps + runningPoetry works; uv has become the default for new projects
Lint + formatRuffreplaces black, isort, flake8, pyupgrade with one config-
Typesmypy --strictProtocol conformance is what makes ports safepyright is a fine swap; strictness is the point
Testspytest + pytest-asyncio (auto mode) + httpx--
Settingspydantic-settingstyped env parsing; lives in bootstrap only-
DocsDocusaurus + Mermaidnavigable site, diagrams in Markdown, GitHub PagesMkDocs Material is a good alternative
CIGitHub Actions, reusable workflow, semantic-releaseConventional Commits drive versions, changelog, tags, GHCR image-

Consequences

  • Inner rings import nothing but the standard library; the architecture tests pin this.
  • Everything above is replaceable from the outside in: HTTP framework in http/ + bootstrap/, ORM in infrastructure/ + shared/infrastructure/, tooling in pyproject.toml.
  • Python 3.12+ is required (PEP 695 generics, StrEnum, Self).

Proof

No executable proof: a technology choice. What the inner rings may import is covered by proof:dependency-rule, not by this decision.