Educational FastAPI service following Clean Architecture with Dishka DI, SQLAlchemy 2.0 ORM, JWT auth, Alembic migrations, and monitoring via Loki + Promtail + Grafana. Ready to run in Docker.
- FastAPI, Pydantic Settings, dishka (DI)
- SQLAlchemy 2.0 (async) + asyncpg, Alembic
- JWT (RS256), pwdlib[argon2]
- uvicorn, uv (package/runner)
- Docker / docker compose
- Loki + Promtail + Grafana (logging)
domain— entities and business rules.application— DTOs, interfaces, use-case services.infrastructure— DB adapters, repositories, migrations, settings.presentation— FastAPI HTTP layer (routes, middlewares, handlers).composition— app wiring, DI container (Dishka), settings loading.
- Python 3.12+ (Docker image uses 3.13)
- uv (https://docs.astral.sh/uv/) or Docker
- Docker Compose v2
Example:
DB_HOST=localhost
DB_PORT=5432
DB_USERNAME=postgres
DB_PASSWORD=postgres
DB_DATABASE=fastapi_example
UVICORN_SERVER_HOST=0.0.0.0
UVICORN_SERVER_PORT=8080
APP_TITLE=FastAPI Example
APP_VERSION=0.1.0
APP_DOCS_URL=/docs
APP_REDOC_URL=/redoc
CORS_ORIGINS=["*"]
CORS_METHODS=["*"]
CORS_HEADERS=["*"]
JWT uses RS256. Keys are read from .certs/jwt-private.pem and .certs/jwt-public.pem. Generate:
mkdir -p .certs
openssl genrsa -out .certs/jwt-private.pem 2048
openssl rsa -in .certs/jwt-private.pem -pubout -out .certs/jwt-public.pem
uv sync
uv run alembic upgrade head
uv run api
Swagger UI: http://localhost:8080/docs
- Prepare
.envand JWT keys as above. - Run API + Postgres:
docker compose --profile api up --build
- Apply migrations (if not already):
docker compose --profile migrations up --build
- Stop:
docker compose --profile api down
lokicollects logs,promtailreads Docker logs,grafanafor dashboards.- Start:
docker compose --profile monitoring up
Grafana: http://localhost:3000 (anonymous access enabled, Loki datasource provisioned via monitoring/grafana/datasourses/grafana-config.yaml).
src/fastapi_example/presentation/v1— routes (/api/v1/users,/api/v1/token), dependencies, exception handlers, CORS.application/services— user and auth business logic, password hashing.infrastructure/database— SQLAlchemy models, repositories, transactions, Alembic migrations.composition/di_container.py— Dishka container wiring.monitoring/*— Loki/Promtail/Grafana configs.
Local:
uv run alembic revision -m "message"
uv run alembic upgrade head
Docker:
docker compose --profile migrations up --build
POST /api/v1/token— issue JWT viausername/password(OAuth2PasswordRequestForm).POST /api/v1/users— register user.GET /api/v1/users— get profile (Bearer token).PATCH /api/v1/users— update profile.DELETE /api/v1/users— delete profile.
- Run server locally:
uv run api - Run format/lint (if configured):
uv run ... - Open Swagger:
http://localhost:8080/docs
Dishka container registers:
DatabaseProvider(engine, sessions, transactions)UsersServiceProvider,HasherServiceProvider,AuthServiceProvider(JWT)
logging.basicConfig sets DEBUG on startup (__main__.py). In Docker, container logs are scraped by Promtail and visible in Grafana (Loki).