Quickstart
Five minutes from git clone to a running app with a freshly scaffolded module.
1. Install
git clone https://github.com/antosubash/simple_module_python.git
cd simple_module_python
make install
cp .env.example .env2. Migrate
make migrateDefault .env uses SQLite — no Docker needed. If you want Postgres, run make docker-up first and edit SM_DATABASE_URL accordingly.
3. Boot
make devThe API and Vite dev servers start side by side. Visit:
http://localhost:8000— landing pagehttp://localhost:8000/users/login— sign-in screenhttp://localhost:8000/dashboard— the authenticated home (log in first)http://localhost:8000/settings/modules— the admin settings UI (log in first)
4. Create an admin
In another terminal:
uv run sm-users create-admin --email admin@example.com --password changemeSign in at /users/login and you land on the dashboard.
5. Scaffold a new module
make new-module name=ordersThis generates modules/orders/ with:
pyproject.toml—[project.entry-points.simple_module]→orders.module:OrdersModuleorders/module.py—ModuleBasesubclass withmeta = ModuleMeta(name="Orders", ...)orders/models.py—OrderSQLModel table withAuditMixinorders/contracts/schemas.py—OrderCreate,OrderOutDTOsorders/service.py— CRUD implementationorders/endpoints/api.py— REST endpoints at/api/ordersorders/endpoints/views.py— Inertia endpoints at/ordersorders/pages/Browse.tsx,Create.tsx,Edit.tsx— React pagesorders/locales/en.json— translation namespacemodules/orders/tests/— pytest test file
The scaffolder registers the package and re-runs uv sync --all-packages, so the module is discoverable on next boot.
6. Generate a migration
make migration msg="add orders tables"
make migrateAlembic's autogenerate picks up the new orders schema (Postgres) or the orders_* tables (SQLite) and writes host/migrations/versions/XXXX_add_orders_tables.py. The first migration of a new module includes a branch_labels = ("orders",) marker so you can downgrade the module in isolation later with alembic downgrade orders@base.
7. Hit the module
Restart make dev (modules are discovered at boot). Then:
curl http://localhost:8000/api/orders
# → [] (200)Visit http://localhost:8000/orders — you see the Browse page with an empty list and a "Create" button. The sidebar menu now includes an Orders entry (registered via register_menu_items).
8. Run the tests
make testRuns Python + JS test suites. Single file:
uv run pytest modules/orders/tests/test_api.py -vWhat just happened
- Discovery — the
simple_moduleentry point pointed Python's installer atorders.module:OrdersModule.discover_modules()loaded it, topologically sorted against every other installed module, and invokedregister_*hooks in order. - Routes —
register_routes(api_router, view_router)attached theordersrouters at/api/ordersand/orders. - Menu —
register_menu_itemspushed an entry ontoMenuRegistry; the Inertia shared-props middleware serialized it intomenus.sidebarfor every authenticated request. - Frontend —
modules.generated.ts(rebuilt bymake gen-pages) maps"Orders/Browse"tomodules/orders/orders/pages/Browse.tsx. Vite resolves and HMR-watches that file. - Database —
create_module_base("orders")namespaced theOrdertable under a Postgresordersschema (or theorders_ordertable name under SQLite).
Where to go next:
- Your first module — stage-by-stage walk-through extending the scaffold to real domain logic.
- Framework overview — what actually happens between
make devand the first HTTP request. - Project structure — the directory tour.