Build scalable, production-ready REST APIs with built-in OIDC authentication, server-side session management, and modern security (rate limiting, CSRF protection, client fingerprinting).
Develop and test like production with a full Docker stack β PostgreSQL, Redis, Temporal, and a local Keycloak instance for dev/test OIDC flows.
In production, use a managed IdP (Azure AD, Okta, Auth0, Google, Cognito, etc.).
A powerful CLI streamlines your workflow β start/stop the dev environment, manage databases, run the API with hot reload, and generate boilerplate for new domain entities (Entity class, ORM model, repository, and router with pre-generated CRUD endpoints).
- Overview
- Key Features
- Requirements
- Quick Start
- Building Your Service
- Built-in Development Environment
- Configuration
- Authentication API
- Testing
- Development Workflow
- Troubleshooting
- Project Structure
- Architecture & Design
- License
- Support
This template provides a complete foundation for building scalable FastAPI applications with:
- π OIDC Authentication (BFF) β Authorization Code + PKCE + nonce, secure sessions, CSRF protection, cookies
- ποΈ Clean Architecture β Entities β Repositories β Services β API layers
- β‘ Complete Dev Environment β Keycloak (dev/test only), PostgreSQL, Redis, Temporal
- π οΈ Developer CLI β Manage env, DB, hot reload, and scaffold entities/routes
- π Cruft Updates β Keep your fork synced with template updates
- ποΈ Flexible Database β PostgreSQL (prod), SQLite (dev/test)
- π Type-safe ORM β SQLModel + Pydantic
- π§ͺ Comprehensive Testing β pytest (unit, integration, E2E)
- BFF pattern with secure, HttpOnly session cookies
- OIDC with multiple providers (Keycloak for dev/test; bring your own IdP for prod)
- PKCE + nonce + state; ID token validation with JWKS caching/rotation
- CSRF protection for state-changing routes; origin allowlist
- Client fingerprinting for session binding
- Rate limiting with Redis
- Sensible CORS and security headers for production
- Docker Compose stack (Keycloak*, PostgreSQL, Redis, Temporal)
- Zero-manual setup with pre-seeded dev realm/users in Keycloak
- Hot reload dev server
- Structured logging with request tracing
- CLI for environment + entity codegen
* Keycloak is dev/test only. In production, configure a managed IdP and point the app to its discovery/issuer URL.
- Clean Architecture layers
- Dependency Injection for testability
- Ruff (format/lint), MyPy (types), pytest (fixtures & E2E)
- Python 3.13+
- Docker & Docker Compose
- uv (recommended) or pip
One-liner:
pip install -U cruft && cruft create https://github.com/piewared/api_project_template
Full steps:
# 1) Create from the template
pip install -U cruft
cruft create https://github.com/piewared/api_project_template
# 2) Configure & run
cd your-project-name
cp .env.example .env
uv run cli dev start-env # Keycloak (dev), PostgreSQL, Redis, Temporal
uv run init-db
uv run cli dev start-server # API w/ hot reload
Local URLs
Service | URL | Notes |
---|---|---|
API | http://localhost:8000 | Dev server (hot reload) |
Docs | http://localhost:8000/docs | OpenAPI/Swagger |
Keycloak* | http://localhost:8080 | Dev/test auth (admin/admin) |
Temporal UI | http://localhost:8081 | Workflows |
- In prod, configure a managed IdP and set
issuer
,client_id
,audiences
, cookiesSecure=true
, etc.
Use the CLI to generate domain entities (model, ORM, repository, router with CRUD) and auto-register routes in the app.
# Create a new entity with interactive field prompts
uv run cli entity add Product
# Manage entities
uv run cli entity ls
uv run cli entity rm Product [--force]
Whatβs generated:
- Entity (domain model + validation)
- Table (SQLModel)
- Repository (CRUD + queries)
- Router (CRUD endpoints)
- Auto-registration with FastAPI
Start here: docs/dev_env/README.md
Dockerized services for local dev/test to quickly spin up a local stack that mimics production:
- π Keycloak β OIDC provider with pre-configured dev realm/users (dev/test only) β docs/dev_env/keycloak.md
- ποΈ PostgreSQL β production-like DB with persistent volume β docs/dev_env/postgres.md
- β‘ Redis β cache, sessions, rate limiting β docs/dev_env/redis.md
- β±οΈ Temporal β workflow engine + UI β docs/dev_env/temporal.md
Common commands:
uv run cli dev start-env
uv run cli dev status
uv run cli dev logs [service]
uv run cli dev stop-env
uv run cli dev start-server
Configuration is centralized in a single config.yaml
, with environment variable overrides (${VAR_NAME:-default}
syntax).
This allows clean defaults under version control, while keeping secrets and environment-specific overrides in .env
.
Layer | Source | Description |
---|---|---|
.env |
Environment variables | Environment-specific values |
config.yaml |
Application config | Structured defaults with env substitution |
FastAPI startup | Pydantic models | Final validation & type safety |
Key sections in config.yaml
:
app
β app metadata, session, CORS, and host configurationdatabase
β DB URL, pool size, timeoutsredis
β cache/session store configtemporal
β background workflowsoidc.providers
β multi-provider authenticationjwt
β token validation rules & claim mappingsrate_limiter
β per-endpoint throttlinglogging
β log level, structured format
-
The OIDC
redirect_uri
(callback) is defined server-side per provider inconfig.yaml
β never accepted from clients. -
Clients may optionally pass a
return_to
param (relative path or allowlisted host) for post-login redirection. -
The application:
- Stores state and PKCE verifier securely (e.g., in Redis).
- Validates
state
andnonce
on callback. - Issues an HttpOnly,
SameSite=Lax
signed session cookie. - Rotates session ID and CSRF token on refresh.
HttpOnly
cookies always (no JS access).- In production,
Secure=true
and HTTPS are mandatory. - For cross-site frontends, set
SameSite=None
+Secure=true
. - Configure
CLIENT_ORIGINS
as a list (comma-separated in.env
).
Prefer discovery:
oidc:
providers:
keycloak:
issuer: http://localhost:8080/realms/test-realm
client_id: test-client
client_secret: test-secret
scopes: ["openid", "email", "profile"]
For production IdPs (Google, Microsoft, Okta, etc.), set:
issuer
to the IdP base URLclient_id
/client_secret
via env varsend_session_endpoint
if your provider supports RP-initiated logout
ENVIRONMENT=development
DATABASE_URL=postgresql://devuser:devpass@localhost:5432/devdb
REDIS_URL=redis://localhost:6379/0
BASE_URL=http://localhost:8000
SESSION_SIGNING_SECRET=change-this-32-char-secret
CLIENT_ORIGINS=http://localhost:3000
OIDC_KEYCLOAK_ISSUER=http://localhost:8080/realms/test-realm
OIDC_KEYCLOAK_CLIENT_ID=test-client
OIDC_KEYCLOAK_CLIENT_SECRET=test-secret
Environment | Provider | Redirect URI | Security |
---|---|---|---|
Dev/Test | Local Keycloak | http://localhost:8000/auth/web/callback |
Self-contained, no internet access |
Production | Managed IdP (e.g., Azure AD, Okta, Auth0, Google) | https://your-api.com/auth/web/callback |
HTTPS required, Secure cookies, rotated secrets |
β In production:
- Replace Keycloak URLs with your IdPβs
issuer
andclient_id
.- Configure OIDC discovery, JWKS validation, and session rotation.
- Set
Secure=true
,SameSite=None
, and a strongSESSION_SIGNING_SECRET
.
All endpoints are under /auth/web
for web clients using session cookies.
GET /auth/web/login
β Initiates OIDC login (uses server-configuredredirect_uri
). Acceptsprovider
(optional) and sanitizedreturn_to
(relative path).GET /auth/web/callback
β Handles OIDC callback. Validatesstate
,nonce
, tokens (issuer/audience/exp/alg via JWKS). Single-use auth session; setsuser_session_id
cookie; redirects toreturn_to
or/
.GET /auth/web/me
β Returns auth state and a CSRF token for subsequent state-changing requests.POST /auth/web/refresh
β Refreshes session (rotates session id + CSRF). RequiresX-CSRF-Token
and Origin allowlist.POST /auth/web/logout
β Logs out (requiresX-CSRF-Token
); optionally supports RP-initiated logout when provider supports it.
Client examples:
uv run pytest
uv run pytest --cov=your_package
uv run pytest tests/unit/
uv run pytest tests/integration/
uv run pytest tests/e2e/
- Unit β business logic
- Integration β DB + external services
- E2E β full auth + workflows
- Fixtures β
tests/fixtures/
- Start Environment:
uv run cli dev start-env
- Verify:
uv run cli dev status
- Start Server:
uv run cli dev start-server
- Access: API/Docs/Keycloak/Temporal via the URLs above
Adding Features
uv run cli entity add EntityName
- Add business logic in services/repositories
- Write unit/integration tests
- Update docs
Debugging
uv run cli dev logs
uv run cli dev logs postgres | keycloak | redis | temporal
Ports in use
sudo netstat -tlnp | grep -E ':8080|:5432'
DB reset
uv run cli dev stop-env
docker volume rm dev_env_postgres_data
uv run cli dev start-env
Keycloak (dev)
curl http://localhost:8080/realms/test-realm/.well-known/openid-configuration
uv run cli dev logs keycloak
Clean reset
uv run cli dev stop-env
docker compose -f dev_env/docker-compose.yml down -v
uv run cli dev start-env
Cookies & cross-site
- If your frontend runs on a different origin, set
SameSite=None
and ensure HTTPS (Secure=true
).
your_project/
ββ src/
β ββ your_package/
β ββ app/
β β ββ entities/ # Domain entities (CLI generates packages here)
β β ββ api/ # FastAPI routers
β β ββ core/ # Auth, DB, config, security
β β ββ runtime/ # App runtime
β β ββ service/ # Domain services
β ββ dev/ # Dev tooling
ββ tests/ # Unit/integration/E2E
ββ dev_env/ # Dockerized infra + volumes
ββ docs/ # Additional docs (clients, guides)
ββ scripts/ # Utilities
Deleting volumes will wipe local data (
dev_env/postgres-data/
, etc.).
- Clean Architecture and DDD-inspired layering
- Entities (domain) β’ Repositories (data access) β’ Services (business logic) β’ API (FastAPI)
- Dependency Injection, Repository Pattern
- Temporal for reliable, long-running workflows
- Type safety end-to-end (Pydantic, SQLModel, MyPy)
MIT β see LICENSE
.
- Open an issue for bugs/features
- Discussions for Q&A/ideas
Quick create
cruft create https://github.com/piewared/api_project_template