A small but complete FastAPI service that shows how to talk to the three storage systems most APIs end up needing, and how to develop, test and ship it:
- PostgreSQL with SQLModel/SQLAlchemy, split into a primary for writes and a streaming replica for reads
- Redis for key/value access, through a single shared connection pool
- S3 (MinIO locally) via
aioboto3, so every store is async end to end - uv for dependency management and reproducible locking
- ruff for linting and formatting, wired into pre-commit
- pytest integration tests that run against the real services
- a CircleCI pipeline: lint, build, Trivy scan, test, publish
Requires Python 3.14 and Docker Compose v2.
docker compose up -d --build --waitNow under localhost:8000/docs you can find the APIs. localhost:8000/health/ready
reports whether the primary, the replica and Redis are all reachable.
Host ports can be remapped if something else already occupies them:
WEB_PORT=8080 MINIO_PORT=9010 MINIO_CONSOLE_PORT=9011 docker compose up -d --waitdocker compose exec web pytestRun a single file with
docker compose exec web pytest src/tests/test_user.pyUse docker compose logs web to check the logs of the FastAPI server, and
docker compose down -v to tear everything down again.
src/app and src/tests are bind-mounted, so code changes are picked up live.
pyproject.toml is not — changing dependencies or pytest settings needs a
docker compose up -d --build.
uv sync --all-groups
uv run ruff check .The tests need the backing services, so run them through Compose as shown above.
Settings are read from the environment (see app/config.py); docker-compose.yml
supplies working defaults. Point VAULT_PATH at an env file to load secrets from a
mounted secret store instead.
| Variable | Meaning |
|---|---|
PRIMARY_DATABASE_URL |
async DSN of the write primary |
REPLICA_DATABASE_URL |
async DSN of the read replica |
REDIS_URL |
Redis connection URL |
REDIS_MAX_CONNECTIONS |
pool size, defaults to 100 |
S3_BUCKET_NAME |
bucket used by the S3 endpoints |
S3_ACCESS_KEY_ID / S3_SECRET_ACCESS_KEY_ID |
S3 credentials |
S3_ENDPOINT_URL |
set for MinIO, leave unset for AWS |
CORS_ALLOW_ORIGINS |
JSON list of allowed origins |
uv run pre-commit install
uv run pre-commit run --all-filesruff and uv lock both run as hooks, so the lockfile can never drift from
pyproject.toml.
The runtime stage is the slim, non-root production image:
docker build --target runtime -t scraiber/fastapi-db-example:v1.0.0 .
docker push scraiber/fastapi-db-example:v1.0.0