A practical Flask scaffold engine with a hypermedia-first approach. It leans on Flask, Jinja2, HTMX, Tailwind CSS and SQLAlchemy to keep application logic close to the UI — no heavy frontend framework unless you actually need one.
Lombik generates a complete application and then gets out of your way. Models, modules, relationships and full CRUD are created from the command line.
pip install lombiklombik createapp myapp
cd myapp
pip install -r requirements.txtcreateapp also initialises the database for you (creates migrations/ and a
local dev.db), so there's nothing else to run:
lombik run # flask run --debug on localhost:5000Then open http://127.0.0.1:5000 — the first-run page creates your owner
account. Prefer the terminal? lombik superuser does the same thing.
By default, local development uses SQLite when DATABASE_URL is empty.
Production uses PostgreSQL via the DATABASE_URL environment variable.
| Command | What it does |
|---|---|
lombik createapp <name> |
Generate a new application (DB initialised automatically) |
lombik run |
Run the development server |
lombik initdb |
Re-initialise the database/migrations manually |
lombik superuser |
Create a superuser interactively |
lombik module <name> |
Generate a blueprint module |
lombik model <name> |
Generate a model and register it |
lombik crud <model> |
Generate a full CRUD blueprint |
lombik relate parent.field to child.field [type] [--lazy lazy] |
Create model relationships |
lombik db -m "message" |
Create a migration and upgrade |
lombik test |
Run the test suite |
lombik model tenant # creates models/tenants.py + registers it
lombik db -m "add tenants" # migrate + upgrade
lombik crud tenant # generates blueprints/tenants/ + templates/tenants/lombik crud maps column types to form fields automatically. Foreign keys
become SelectField dropdowns, booleans become checkboxes, and dates/numbers
use the right input types.
myapp/
app.py # app factory + module-level `app`
db.py # SQLAlchemy + Migrate instances
application/ # scaffold runtime (auth, forms, filters, crud, themes, ...)
blueprints/
auth/ # login, register, password reset
core/ # home + example HTMX partials
admin/ # owner onboarding + superuser admin panel
models/ # SQLAlchemy models
templates/ # Jinja2 templates
static/ # css, js, icons
tests/ # pytest suite
Dockerfile
docker-compose.yml
Procfile # Railway
railway.json
The generated runtime package is called application, so your app code
reads from application... and never collides with the lombik CLI package.
Each blueprint keeps two concerns: pages handle HTTP, services do the
real work. Add a forms.py only when a module needs forms.
blueprints/
auth/
__init__.py # the Blueprint object
pages.py # page handlers + mutations
forms.py # login/register form definitions
core/
__init__.py
pages.py
Page handlers stay thin:
# blueprints/core/pages.py
from application.wrappers import login_required, roles_required
@core_bp.route("/members")
@login_required
def members():
return render_template("core/members.html")
@core_bp.route("/admin")
@login_required
@roles_required("admin", "superuser")
def admin():
return render_template("core/admin.html")Business logic lives in a service so it can be reused and tested without Flask:
# application/users.py (or a blueprint's services.py)
from application.extensions import cache
from models import User
@cache.memoize(timeout=30)
def get_user_by_email(email):
return User.query.filter_by(email=email.strip().lower()).first()Pages are server-rendered; interactions return fragments. A typical pattern:
<section hx-get="{{ url_for('core_bp.quickstart') }}"
hx-trigger="load"
hx-swap="innerHTML">
Loading…
</section>The built-in htmx_response() helper sets HX-Trigger and HX-Redirect
headers. The CSRF token is injected into every HTMX request automatically by
static/js/csrf.js.
On a fresh database the app redirects to /admin, a graphical owner-onboarding
page. It creates the first superuser account and signs you in. Prefer the
terminal? The equivalent command still exists:
lombik superuserOnce an owner exists, /admin becomes a live, HTMX-powered admin panel for
superusers, split into four pages:
- Errors (the main page) — stats, day-by-day and hour-by-hour error charts (using the bundled chart elements), and the full error log newest first, auto-refreshing every few seconds. Each record captures the exception type, message, traceback, function, request method/path, IP, user and optional tenant id.
- Schema — collapsible tables with their fields, types and constraints.
- Appearance — activate a built-in theme or create a custom one with color pickers, name it, and save it to the filesystem.
- Guide — a markdown handbook covering the structure, ideology and the recommended build flows.
Themes live in application/themes.py and are rendered as CSS custom
properties. The active theme and any custom themes are stored in
instance/theme.json — a local filesystem file, not the database. Built-in
themes ship with the scaffold; custom themes can be created, activated and
deleted from the admin panel.
Generated apps ship with a small semantic token set driven by CSS variables in
static/css/global.css. Dark mode is automatic (.dark swaps the variables).
| Token | Meaning |
|---|---|
canvas |
Page background |
surface |
Panel / card background |
raised |
Inputs and elevated surfaces |
line |
Hairline borders |
ink |
Primary text |
ink-muted |
Secondary text |
brand |
Accent / primary action |
success / warning / danger |
Semantic states |
Use bg-canvas, bg-surface, border-line, text-ink, text-ink-muted,
bg-brand, bg-ink text-canvas, etc. Raw hex values should not appear in
templates — reuse the tokens so light/dark always works.
Included, dependency-free, in static/js/:
- Drag — reorderable lists, kanban slot swapping, cross-list moves and
free-floating elements. See
static/js/drag.jsfor the full API. - Dropdowns — the custom
<dropdown>element with viewport-aware positioning. - Modals —
openModal('id')/closeModal('id'). - Flash — auto-dismissing toasts with
Flash.ok(),Flash.error(), etc. - Charts — custom
<bar-chart>,<line-chart>,<donut-chart>and<bubble-chart>elements instatic/js/chart.js.
docker compose up --buildThis starts Postgres and the web app. The web service sets DATABASE_URL
automatically.
The repo includes a Dockerfile, Procfile and railway.json. Railway uses
the Dockerfile by default and runs the release command (flask db upgrade)
on each deploy. Add a Postgres service and set DATABASE_URL, SECRET_KEY and
CRKEY as service variables.
lombik test # pytest
lombik test_report # pytest + coverage
lombik test_report_html # pytest + HTML coverage reportThe default test suite covers string helpers, date utilities, form validation, security helpers, responses, and the built-in validators.
- Keep logic close to the UI with server-rendered HTML and HTMX.
- Keep
pages.pythin and put real work in services. - Generate boilerplate from the command line, then edit real files.
- Keep the graphical surface minimal: a single owner admin panel.
- Ship sensible, secure defaults: hashed passwords, CSRF, sessions, rate
limiting and structured
Resultresponses.
MIT licensed — contributions, fixes, ideas and forks are welcome.