Server-driven admin UI: the FastAPI + HTMX backend (SQLite books, session/CSRF, Bearer API) with the Bootstrap 5 admin shell as the browser UI.
This is the FastAPI HTMX starter with that shell in place of the Pico UI. The books API, session, and roles are the same.
Browsers get HTML (Jinja + Bootstrap 5.3 + HTMX 4.0.0). Machines get /api with Bearer tokens. One user store, Docker Compose, pytest.
Architecture: docs/ENGINEERING.md.
Sessions, CSRF, and Bearer: docs/auth.md.
Vendored Bootstrap, Bootstrap Icons, and Chart.js notices: LICENSES.md.
| Role | |
|---|---|
| Primary | SQLite books · roles (viewer / editor / admin) · session + CSRF for /ui and /auth · Bearer (or session) for /api/books · Bootstrap admin shell + HTMX |
| Demos (thin, removable) | /items in-memory CRUD · /db-items MongoDB CRUD |
Requires: Docker Compose. Make is optional. Dependencies are managed with uv (pyproject.toml + uv.lock).
make dotenv # .env.example → .env
make upd # build + start (detached)
# http://localhost:8000 → /ui/dashboard (login first)
# http://localhost:8000/auth/login
# http://localhost:8000/ui/books → books CRUD
# http://localhost:8000/docs → OpenAPI (JSON API only)Demo user (seeded when users is empty) is an admin: DEMO_USERNAME / DEMO_PASSWORD (defaults admin / admin123). Change these and SECRET_KEY before any shared deploy.
make seed # optional viewer + editor users and sample books
make test
make downuv sync --all-groups
uv run fastapi dev app/main.py --port 8000 --host 0.0.0.0
uv run pytestThe shell is app/ui/templates/layout.html (navbar, sidebar, footer). Sample pages keep their own routes:
| Page | Path |
|---|---|
| Dashboard | /ui/dashboard |
| Users, Forms, Tables | /ui/users, /ui/forms, /ui/tables |
| Components, Cheatsheet, Palette lab | /ui/components, /ui/cheatsheet, /ui/palette-lab |
| Settings, Blank, 404 | /ui/settings, /ui/blank, /ui/404 |
| Header specimens | /ui/headers/simple (and centered, buttons, dark, account, product, double, icons) |
| Sidebar specimens | /ui/sidebars/dark (and light, icons, collapsible, list-group) |
| Sign in | /auth/login |
Working data pages use the same shell:
| Page | Path |
|---|---|
| Books | /ui/books |
| Book | /ui/books/{id} |
| New book | /ui/books/new |
| Edit book | /ui/books/{id}/edit |
| Search books | /ui/books/search |
| Staff (admin) | /ui/admin/users |
The sidebar is split into App (the product) and Samples (the original Bootstrap pages). To add a page, copy app/ui/templates/pages/blank.html, register it in SHELL_PAGES in app/ui/pages_routes.py, and add a sidebar link under the matching group in app/ui/templates/partials/sidebar.html.
- Browser / HTMX: cookie session + CSRF on mutating HTML (
/auth,/ui). - API: Bearer via
POST /api/auth/token, or session cookie. Session writes to/apineedX-CSRF-Token. Bearer skips CSRF.
curl -s -X POST http://localhost:8000/api/auth/token \
-H 'Content-Type: application/json' \
-d '{"username":"admin","password":"admin123"}'app/
auth/ # passwords, users, tokens, deps
db/ # SQLite connection + schema (primary)
routes/ # JSON API (/api/..., demos)
ui/ # HTML/HTMX templates + Bootstrap static
models/ # Pydantic schemas
core/ # settings + logging
docs/
pyproject.toml
uv.lock
MIT — see LICENSE. Third-party notices for vendored front-end files are in LICENSES.md.