2026-06-22 14:56:51 -03:00
2026-06-22 14:56:51 -03:00
2026-06-22 14:56:51 -03:00
2026-06-22 14:56:51 -03:00
2026-06-22 14:26:44 -03:00
2026-06-22 14:26:44 -03:00
2026-06-22 14:26:44 -03:00
2026-06-22 14:26:44 -03:00
2026-06-22 14:26:44 -03:00
2026-06-22 14:26:44 -03:00
2026-06-22 14:56:51 -03:00
2026-06-22 14:26:44 -03:00
2026-06-22 14:26:44 -03:00
2026-06-22 14:56:51 -03:00

FastAPI Skeleton

A production-shaped starting point for a web app that serves both HTML (HTMX) and a JSON API.

Stack

Concern Choice
Web framework FastAPI
HTML / interactivity Jinja2 + HTMX
ORM SQLAlchemy 2.0 (async)
Migrations Alembic
Database SQLite (default) or Postgres — one DATABASE_URL
Auth JWT access + refresh tokens, rotation, DB-backed revocation
Authorization RBAC: users → roles → permissions
Admin UI Custom HTMX dashboard (user & role CRUD)
Config env vars or .env (pydantic-settings)
Tooling uv, pytest, ruff
Packaging Dockerfile + docker-compose

Layout

app/
  core/        config, database engine/session, security (hashing + JWT)
  models/      SQLAlchemy models: user, role, permission, refresh_token
  schemas/     Pydantic request/response models
  crud/        data-access helpers
  services/    auth token issuance / rotation
  deps.py      DI: get_db, current user, require_permission(...)
  api/         JSON API: auth, users, roles
  web/         HTML/HTMX routes: pages + admin dashboard
  templates/   Jinja2 templates (+ admin partials)
  static/      CSS
alembic/       migration environment + initial migration
scripts/seed.py  bootstrap permissions, roles, first superuser
tests/         pytest (auth + RBAC)

Quick start (local, SQLite)

# 1. Install dependencies (https://docs.astral.sh/uv/)
uv sync --extra dev

# 2. Configure (optional — sensible defaults exist)
cp .env.example .env        # then edit SECRET_KEY etc.

# 3. Seed default roles/permissions + the first superuser
uv run python -m scripts.seed

# 4. Run
uv run uvicorn app.main:app --reload

Open:

Default superuser (override via env/.env): admin@example.com / changethis.

Run with Docker (Postgres)

docker compose up --build

This starts Postgres, seeds the database, and serves the app on http://localhost:8000. The app connects with DATABASE_URL=postgresql+asyncpg://app:app@db:5432/app.

Configuration

Every setting is read from an environment variable first, then .env (see .env.example). Key ones:

Variable Default Notes
SECRET_KEY dev placeholder set a strong value in production
DATABASE_URL sqlite+aiosqlite:///./app.db use postgresql+asyncpg://... for Postgres
ACCESS_TOKEN_EXPIRE_MINUTES 15
REFRESH_TOKEN_EXPIRE_DAYS 7
AUTO_CREATE_TABLES true create tables on startup (dev); use Alembic in prod
COOKIE_SECURE false set true behind HTTPS
FIRST_SUPERUSER_EMAIL / _PASSWORD admin@example.com / changethis used by seed

Auth model

  • POST /api/auth/register — self-service signup (gets the default user role)
  • POST /api/auth/login — OAuth2 password form (username = email) → access + refresh
  • POST /api/auth/refresh — rotates: the presented refresh token is revoked, a new pair is issued
  • POST /api/auth/logout — revoke one refresh token (body) or all for the user

The JSON API authenticates via Authorization: Bearer <access>. The HTMX UI stores the access token in an httpOnly cookie so pages work without client JS.

Refresh tokens are stored server-side as a SHA-256 hash + jti with a revoked flag, so logout and rotation genuinely invalidate them.

RBAC

users → roles → permissions. Permissions follow resource:action (users:read, users:write, users:delete, roles:read, roles:write). Protect an endpoint with the dependency factory:

@router.get("", dependencies=[Depends(require_permission("users:read"))])
async def list_users(...): ...

Superusers (is_superuser=True) bypass all permission checks.

Migrations (Alembic)

AUTO_CREATE_TABLES is convenient for dev/SQLite. For real deployments use migrations instead:

# apply the bundled initial migration
uv run alembic upgrade head

# after changing models, autogenerate the next revision
uv run alembic revision --autogenerate -m "describe change"
uv run alembic upgrade head

The Alembic env reads DATABASE_URL from app settings, so it targets the same database as the app.

Tests

uv run pytest

Tests run against an in-memory SQLite database and cover the auth lifecycle (register/login/me/refresh/logout) and RBAC enforcement.

Where to go next

  • Add your own models in app/models/, schemas in app/schemas/, routes in app/api/ or app/web/.
  • Add permissions to scripts/seed.py and guard routes with require_permission(...).
  • Swap HS256 for asymmetric RS256 keys if you need to verify tokens in other services.
S
Description
No description provided
Readme
466 KiB
0 Stars 1 Watchers 1 Forks
Languages
Python 83.9%
HTML 10.1%
CSS 4.3%
Mako 0.7%
Dockerfile 0.7%
Other 0.3%