---
description: FastAPI service conventions
globs: ["**/*.py"]
alwaysApply: true
---

# FastAPI

- Python 3.12+, full type hints on every function. Run `ruff check --fix` and `mypy --strict`.
- Layout: `app/api/routes/*` (thin routers) → `app/services/*` (business logic) → `app/repositories/*` (DB).
- Request and response bodies are Pydantic v2 models in `app/schemas/`. Never return ORM objects directly; set `response_model`.
- Use `async def` endpoints with SQLAlchemy 2.0 `AsyncSession` injected via `Depends(get_session)`.
- Settings via `pydantic-settings` `BaseSettings`; no `os.getenv` scattered in code.
- Raise `HTTPException` only in routers; services raise domain exceptions mapped by an exception handler.
- Migrations with Alembic: `alembic revision --autogenerate -m "..."`, then review the diff.
- Tests: pytest + `httpx.AsyncClient` with `ASGITransport`; each new endpoint needs a happy-path and an error-path test.
- Log with `structlog`/`logging`, never `print`.
