ADR 0001: Litestar as the HTTP framework¶
- Status: Accepted
- Date: 2026-10-02 (decision originally taken on 2026-06-21, in
8a523fe)
Context¶
The project needed an HTTP API to expose the archive domain to the dashboard and to
future clients. TODO.md had planned FastAPI, and that line survived in the roadmap
long after the implementation went the other way, which made the choice look
accidental to anyone reading the repository.
The decision was taken when the taxonomy endpoint was built (8a523fe). The
requirements that shaped it were specific:
- A single, declarative place to map domain exceptions to HTTP status codes, so controllers stay free of translation logic.
- Dependency injection tied to the request, because every service must be built over the request's session and transaction.
- Per-route control over thread offloading. Nearly every route is synchronous and
performs blocking database I/O, so blocking the event loop is the default failure
mode; the taxonomy route additionally dispatches CPU-bound clustering work to a
subprocess through
anyio.to_process.
Decision¶
Use Litestar. FastAPI was never a dependency of this project at any point; the API layer was created directly on Litestar.
Rationale¶
The requirements above are first-class features in Litestar rather than patterns the application has to build:
exception_handlerskeyed by exception class is exactly the shape ofapi/handlers.py, and is what letsdomains/raiseDomainExceptionsubclasses without knowing HTTP.Providecomposes per-request dependencies, which is what makesprovide_unit_of_workinapi/dependencies.pya real composition root instead of a convention every handler must remember.sync_to_threadis explicit per route, so the blocking I/O of the repositories is offloaded deliberately, and the async routes are visibly different from the sync ones.- Pydantic v2 is consumed directly instead of being coupled to the framework, which
matches the domain DTOs in
domains/*/schemas/.
Consequences¶
Positive: the API layer is thin, the domain stays framework-agnostic, and the transaction has one owner shared with the workers.
Negative, and accepted:
- Smaller ecosystem. Far fewer tutorials, examples and Stack Overflow answers than FastAPI. This is not hypothetical: parameter inference deprecations shipped in Litestar 2.24 went unnoticed here precisely because no external material pointed at them, and they had to be found by reading the framework source. The suite now fails loudly on those warnings.
- Litestar 3.0 will require migration work. The deprecated forms are already gone
from the code (see
3bea03f), so the remaining exposure is unknown-unknowns rather than the known ones.
Alternatives considered¶
- FastAPI. Rejected: more ecosystem, but the DI/exception-handling/thread-offload requirements would each need application-level scaffolding, and the project would trade real working code for a rewrite with no functional gain.
- Migrating now. Rejected on the same grounds: the learning cost is already paid and the code is idiomatic Litestar. Revisit only if the ecosystem gap starts costing more than the framework features save.
- Starlite/Django Ninja and similar. Not evaluated in depth; the project has no Django footprint and the requirements above are covered by Litestar.
Revisit trigger¶
Reconsider if any of these becomes true: the API grows consumers outside this institution that demand FastAPI-specific tooling (for example OpenAPI-based SDK generation with mature generators), or Litestar 3.0 raises a migration cost that exceeds a rewrite.