Quickstart
Five minutes from smpy new to a running app with a freshly scaffolded module.
1. Install the CLI
uv tool install simple_module_cli(Or pipx install simple_module_cli.) That puts smpy on your PATH globally.
2. Scaffold an app
smpy new myapp --yes
cd myapp--yes accepts the defaults (SQLite, no multi-tenancy, the standard preset: auth, users, dashboard, permissions). The CLI runs uv sync, npm install, and alembic upgrade head for you.
For an interactive run with prompts, drop the --yes. For a preset + extras: smpy new myapp --preset standard --with background_tasks,file_storage --yes.
3. Boot it
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; only when thesettingsmodule is installed, e.g. thefullpreset or--with settings)
4. Create an admin
In another terminal, from inside myapp:
uv run smpy users create-admin --email admin@example.com --password changemeSign in at /users/login and you land on the dashboard.
5. Scaffold a new module
smpy create-module orders --dest modules/orders
uv add ./modules/ordersThis generates a publishable starter module at modules/orders/ with:
pyproject.toml—[project.entry-points.simple_module]→orders.module:OrdersModulepackage.json+tsconfig.json— JS workspace metadata sotsc --noEmitcovers the module's.tsxpages.orders/module.py—ModuleBasesubclass withmeta = ModuleMeta(name="Orders", ...), wiringregister_routesandregister_settings.orders/services.py— module-scoped state container (stored onapp.state.ordersbyregister_settings).orders/settings.py— the module'spydantic_settingssettings class.orders/endpoints/api.py— starter REST endpoints at/api/orders.orders/pages/— empty page dir; add.tsxpages (and aregister_routesview router) as you build views.tests/test_module.py— pytest smoke test.
You add the domain model (models.py), DTOs (contracts/), service, Inertia views, and pages yourself — the first-module guide walks through that.
uv add ./modules/orders adds the package to your app's dependencies; on the next uv sync (which uv add also runs) the entry point is registered and the module becomes discoverable on next boot.
6. Generate a migration
make migration msg="add orders tables"
make migratemake migration runs Alembic from the host directory (where alembic.ini lives). Autogenerate picks up the new orders_* tables and writes host/migrations/versions/XXXX_add_orders_tables.py. Add branch_labels = ("orders",) to that revision so you can later alembic downgrade orders@base to roll the module back in isolation.
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
uv run pytestSingle file:
uv run pytest modules/orders/tests/test_orders.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")gave the module its own SQLModelMetaDataso Alembic can attribute theorders_ordertable to it.
Next steps
- Your first module — extend the scaffold into real domain logic, end-to-end.
- Project structure — the directory tour, so you know where everything lives.
- Framework overview — what happens between
make devand the first HTTP request. - Bundled modules — what's already in the box (auth, users, permissions, settings, …).