Baseline – a production FastAPI starter kit
What a "production-ready" FastAPI starter actually needs Every FastAPI project I've started begins the same way: an hour of boilerplate before I write a single line of actual logic. Auth. A database session dependency. A folder structure that won't fall apart once there's more than one resource. A test setup that doesn't take longer to configure than the tests themselves. I got tired of rebuilding it, so I built it once, properly, and wrote down why each piece is shaped the way it is. The structure Every resource in the project follows the same four layers: Router — HTTP in/out only. Parses the request, calls a service, serializes the response. No business logic lives here. Service — business rules. Ownership checks, "does this already exist" decisions, orchestration. No FastAPI imports — this layer doesn't know it's running inside a web framework. Repository — persistence only. SELECT/INSERT/UPDATE/DELETE via SQLAlchemy. No business rules. Schema — Pydantic models for request/response shapes, kept separate from the ORM models. This feels like overkill for a single resource. It stops feeling that way the first time you need the same ownership check enforced in two different routes, or the first time you want to unit-test a business rule without spinning up the whole ASGI app to do it. The decisions that actually mattered Testing against real Postgres, not SQLite. A SQLite-backed test suite gives you false confidence — native UUID types, enum handling, and constraint behavior all differ enough that "tests pass" stops meaning "the Postgres-specific code works." Each test runs inside a SAVEPOINT that gets rolled back afterward, so isolation doesn't cost a schema rebuild per test. Two token types, not one. Short-lived access tokens (15 min) plus longer-lived refresh tokens (30 days), with the token's type claim checked on every decode — a refresh token presented where an access token is expected gets rejected on that alone, not just on signature validity. One error shap