The fundamental Rust web framework. Forged by Empu.
Purwa is an opinionated Rust web framework from Sangkan: Laravel-class developer experience (conventions, routing, ORM path, auth, CLI scaffolding) on Axum and Tower, with Svelte + Inertia as the default full-stack story.
- Getting started — ~15 minute first run (
empu new,cargo run,/health) - Architecture — crates, request flow, config, SQLx vs SeaORM
- Escape hatches — raw Axum/Tower,
Router+ state, SQLx - Queue — Redis-backed jobs (Phase 2 MVP)
- MVP checklist — PRD §11 verification pointers
- PRD.md — product requirements and architecture
- TASK.md — sprint plan and acceptance criteria
- AGENT.md — guidelines for contributors and AI agents
- CONTRIBUTING.md — build, test, PR expectations
- CHANGELOG.md — release notes (semver)
Primary: Linux and macOS. Native Windows development is not a v1 goal (see PRD); paths in tooling may assume Unix.
Settings load from optional purwa.toml in the working directory and from environment variables prefixed with PURWA, with nested keys separated by __ (for example PURWA_SERVER__PORT). A .env file is read via dotenvy when present. See purwa.toml.example and .env.example.
Database URL: set [database].url in purwa.toml, or PURWA_DATABASE__URL, or DATABASE_URL. Use this for PgPool, AppState, and empu migrate (Sprint 4).
Integration tests (TASK Q4 — two layers):
- Without Postgres: exercise handlers and routing only. Use
purwa-testing(oneshot,oneshot_status,json_body, …) withrouter_from_inventoryor a manualRouter<()>. There is no lightweight mock forsqlx::PgPool; keep fast tests free of a real pool (route-only / stubExtensiontypes), or use layer (2). - With Postgres: framework crates (e.g.
purwa-orm) use testcontainers where noted (requires Docker). For a fixed instance instead, setTEST_DATABASE_URLto a disposable database (see .env.example). Example migration files: purwa-orm/tests/fixtures/migrations (copy into your app’sdatabase/migrations). Optional: enablepurwa-testingfeaturepostgresforwith_testcontainer_postgres— still usepurwa_orm::connect_pool/migrate_*as the single source of truth for migrations (see purwa-orm/tests/migrate_integration.rs).
Scaffolded apps from empu new include tests/no_db_smoke.rs and an ignored tests/postgres_optional.rs demonstrating both paths.
Merging inventory-based routes (router_from_inventory) with a router that uses AppState (typed Router<AppState>) is a composition detail for your main (Sprint 4+ may refine helpers); handlers that need config should use State<Arc<AppConfig>> with AppState and Axum FromRef.
Use the validator crate (#[derive(Validate)] on request DTOs) with ValidatedJson or ValidatedForm. Failed rules return 422 with JSON:
{ "message": "Validation failed", "errors": { "field_name": ["…"] } }
Malformed JSON (400) uses { "message": "…" }. PurwaError implements IntoResponse for use in Result-returning handlers. Scaffold a DTO with empu make:request CreateThing (writes src/app/http/requests/create_thing.rs by default).
PurwaError covers validation, malformed JSON/form, 401 / 403 / 404, sqlx::Error (row-not-found maps to 404; details are logged, not returned), and generic 500. Library crates should use thiserror and surface PurwaError at HTTP boundaries; application binaries may use anyhow in main (startup, glue) and convert to PurwaError before returning from handlers.
Inertia: use InertiaRequest::respond_purwa_error with the shared Error page (INERTIA_ERROR_COMPONENT, generated as Pages/Error.svelte) so X-Inertia JSON and full-page HTML stay aligned.
Tracing: call purwa::init_tracing() once at startup (after dotenvy::dotenv()). Uses pretty logs by default; set PURWA_ENV=production for JSON lines. Levels follow RUST_LOG (default info via init_tracing_with_filter).
Enable the adapter with purwa = { path = "...", features = ["inertia"] }. Crate purwa-inertia implements protocol v1.3: InertiaRequest extractor, InertiaRenderContext + InertiaRequest::respond (JSON vs HTML first load with optional Vite tags via html_body_injection), partial reload headers, 409 on asset version mismatch for GET, and shared props middleware. Set [inertia].asset_version in purwa.toml (or PURWA_INERTIA__ASSET_VERSION); empu build can sync it from public/.vite/manifest.json after the Vite build. Use empu new --inertia or empu inertia:setup for the frontend/ template.
Purwa registers HTTP handlers with the inventory crate (linker sections). That mechanism is not supported on wasm32 targets; use Purwa on native server/desktop targets only for macro-based routing.
Purwa — the fundamental Rust web framework. Forged by Empu.
In Javanese cosmology, Purwa is the first movement of the Purwa–Madya–Wasana cycle: the beginning, the right place to start. Sangkan (the source) names the organization behind the project. Empu is the master smith who tempers raw iron into a pusaka — something built to last. The CLI Empu is that forge for your app: conventions, generators, and Laravel-class productivity on Axum and Tower, without hiding the platform when you need it.
The north star: a developer comfortable with Laravel should feel productive in under one day; the stack stays idiomatic Rust (memory safety, async on Tokio, escape hatches to Axum and SQLx).
Purwa — kerangka kerja web Rust fundamental. Ditempa oleh Empu.
Dalam kosmologi Jawa, Purwa adalah fase pertama Purwa–Madya–Wasana: permulaan, titik awal yang benar. Sangkan adalah sang sumber — organisasi yang membawa visi ini. Empu adalah pandai besi agung yang menempa besi menjadi pusaka, perangkat lunak yang tahan lama. CLI Empu adalah palu dan dapur tempa untuk aplikasi Anda: konvensi, scaffolding, dan pengalaman mirip Laravel di atas Axum dan Tower, tanpa menutup akses ke lapisan bawah ketika Anda membutuhkannya.
Bintang utara: pengembang yang nyaman dengan Laravel produktif dalam kurang dari satu hari; tetap Rust yang idiomatis — aman memori, async Tokio, dan jalan keluar ke Axum serta SQLx.
MIT — see LICENSE.