How to Run FormSpec
FormSpec berjalan dalam satu proses engine (formspec dev) + frontend (SPA React). App business logic dalam bahasa apapun (Go, PHP, Python, Ruby, Java, .NET, TypeScript, Rust) berjalan sebagai child process.
Belum install CLI
formspec? Lihat install.md — installer script satu perintah tanpa Go, ataugo installuntuk Go developer.
| Opsi | Cara | Terminal | HMR | Cocok untuk |
|---|---|---|---|---|
A — --dev-ui | formspec dev spawn Vite otomatis | 1 | ✅ | Development paling praktis |
| B — Manual | formspec dev + npm run dev | 2 | ✅ | Development |
| C — Static | formspec dev + --web-dir | 1 | ❌ | Demo / produksi |
Opsi A: Satu Terminal — --dev-ui
go run ./cmd/formspec/ dev \
--spec examples/Clinic-UI-Showcase/spec \
--dsn "sqlite:.formspec/clinic.db" \
--addr :8080 \
--force --dev-uiformspec dev akan:
- Kill engine sebelumnya (kalau ada) berkat
--force - Load engine + REST API di
:8080 - Spawn
npm run devsebagai child process — Vite HMR siap di:5173 - Saat
Ctrl+C, Vite ikut dimatikan
Buka http://localhost:5173/default/\_admin.
Opsi B: Dua Terminal
Terminal 1 — Engine:
go run ./cmd/formspec/ dev \
--spec examples/Clinic-UI-Showcase/spec \
--dsn "sqlite:.formspec/clinic.db" \
--addr :8080 \
--dev --forceTerminal 2 — Frontend (Vite HMR):
cd renderers/react-shadcn && npm run devOpsi C: Static SPA (tanpa Vite)
cd renderers/react-shadcn && npm run build
go run ./cmd/formspec/ dev \
--spec examples/Clinic-UI-Showcase/spec \
--dsn "sqlite:.formspec/clinic.db" \
--addr :8080 \
--dev --force \
--web-dir renderers/react-shadcn/distSatu port :8080 untuk API + SPA. Buka http://localhost:8080/default/\_admin.
Setiap edit frontend perlu
npm run buildulang.
Flags
| Flag | Fungsi | Default |
|---|---|---|
--spec | Path ke direktori YAML manifests | ./spec |
--dsn | Database DSN (sqlite atau postgres) | sqlite:.formspec/data.db |
--addr | REST API listen address | :8080 |
--dev | Dev mode (hot-reload, unsigned artifacts) — auth tetap JWT asli, seragam dengan prod | false |
--jwt-secret | HMAC secret untuk JWT signing. Kosong di dev → auto-generate + persist ke .formspec/dev-jwt-secret; kosong di prod → error | auto |
--state-dir | Local state directory | .formspec |
--force | Kill previous formspec engine on same ports | false |
--web-dir | Built SPA directory (e.g. renderers/react-shadcn/dist) | "" |
--dev-ui | Spawn npm run dev otomatis (implikasikan --dev) | false |
--runtime | App runtime: auto, go, php, python, ruby, java, dotnet, rust, node | auto |
--app-dir | App source directory (child-process runtime) | .formspec/app |
--app-entrypoint | Entrypoint file (default tergantung runtime) | auto |
--listendan--app-endpointsudah otomatis diatur olehformspec dev— tidak perlu di-set manual.
Reset Database
rm -rf .formspecDatabase auto-generate saat engine restart.
Troubleshooting
| Masalah | Solusi |
|---|---|
| Port already in use | Tambah --force |
| Blank page | Hard refresh (Ctrl+F5) |
| Permission denied | formspec dev pilih socket/HTTP otomatis berdasarkan environment |
| Hyphen di tabel SQL | Engine otomatis - → _ (fix di internal/db/crud.go) |
Prasyarat
| Tool | Minimal | Cek |
|---|---|---|
| Go | 1.26+ | go version |
| Node.js | 22+ | node --version |
| npm | 10+ | npm --version |
1. Engine — formspec dev
formspec dev adalah engine yang:
- Load YAML manifest dari direktori
--spec - Generate tabel SQLite/Postgres sesuai entity spec
- Serve REST API di
/{workspace}/api/v1/...(permukaan eksternal,spec.expose) - Serve REST API sesi di
/{workspace}/_ui/entity/...(permukaan UI) - Serve Meta API di
/{workspace}/_ui/_meta/... - Auto-detect runtime dari
--app-dirdan spawn app child process - Enforce permission, tenant isolation, ctx.* primitives
Command
# Dari root repository
cd /workspaces/formspec
go run ./cmd/formspec/ dev \
--spec examples/Clinic-UI-Showcase/spec \
--dsn "sqlite:.formspec/clinic.db" \
--addr :8080 \
--devApp Child Process
App business logic dalam bahasa apapun berjalan sebagai child process dari formspec dev. Engine dan app berkomunikasi via Unix socket:
┌───────────────────────────────────────────┐
│ formspec dev (engine) │
│ • Entity engine, state machine │
│ • Permission enforcement │
│ • Tenant isolation │
│ • REST API + Admin panel │
│ • ctx.* primitives │
│ │ │
│ ▼ Unix socket │
│ app child process (via lib-formspec-*) │
│ • Business logic only │
│ • Go / PHP / Python / Ruby / Java │
│ .NET / TypeScript / Rust │
└───────────────────────────────────────────┘Auto-detect Runtime
formspec dev mendeteksi runtime dari file di --app-dir:
| File | Runtime |
|---|---|
go.mod | Go |
Cargo.toml | Rust |
package.json | Node.js / TypeScript |
composer.json | PHP |
pyproject.toml / requirements.txt | Python |
Gemfile | Ruby |
pom.xml / build.gradle | Java |
*.csproj / *.sln | .NET |
Override manual dengan --runtime <name> atau runtime: di formspec-app.yaml.
Flags
| Flag | Fungsi | Default |
|---|---|---|
--spec | Path ke direktori YAML manifests | ./spec |
--dsn | Database DSN (sqlite atau postgres) | sqlite:.formspec/data.db |
--addr | REST API listen address | :8080 |
--dev | Dev mode (auth bypass, unsigned artifacts) | false |
--state-dir | Local state directory | .formspec |
--force | Kill previous formspec engine on same ports | false |
--web-dir | Built SPA directory | "" |
--dev-ui | Auto-spawn Vite dev server | false |
--runtime | Override runtime auto-detect | auto |
--app-dir | App source directory | .formspec/app |
--app-entrypoint | Entrypoint file | auto |
--listendan--app-endpointsudah diatur internal — tidak perlu di-set manual.--app-endpoint-urluntuk override jika perlu endpoint spesifik.
--force Flag
--force otomatis membunuh proses formspec sebelumnya yang masih menempel di port yang sama, lalu restart yang baru. Jika port dipakai program lain, akan muncul error:
port 8080 is already in use by "nginx" (PID 12345).
Use --force to kill a previous formspec instance, or stop the other program manuallyContoh: Clinic UI Showcase
mkdir -p .formspec
go run ./cmd/formspec/ dev \
--spec examples/Clinic-UI-Showcase/spec \
--dsn "sqlite:.formspec/clinic.db" \
--addr :8080 \
--dev \
--forceOutput:
port 8080 is held by a previous formspec instance (PID 12345) — killing it...
[formspec] engine loaded: 45 routes
[formspec] ctx listener on unix:///tmp/formspec/sidecar.sock
[formspec] REST API on :8080Verifikasi:
curl http://localhost:8080/default/_ui/_meta/me
# → {"data":{"user_id":"developer","workspace":"default","permissions":["*"]}}2. Frontend — Vite Dev Server
Frontend adalah SPA React 19 yang membaca Meta API secara runtime.
Command
cd renderers/react-shadcn
# Install dependencies (pertama kali)
npm install
# Jalankan dev server
npm run devVite Proxy
Vite perlu proxy API calls ke backend. Konfigurasi ada di renderers/react-shadcn/vite.config.ts:
server: {
proxy: {
// Kunci yang diawali "^" diperlakukan Vite sebagai RegExp. Wajib, karena
// SPA berjalan di workspace apa pun yang disebut URL — setiap API call-nya
// `/{ws}/_ui/...` atau `/{ws}/api/v1/...`, bukan selalu `default`.
'^/[a-z0-9-]+/api/v1': {
target: 'http://localhost:8080',
changeOrigin: true,
ws: true,
},
'^/[a-z0-9-]+/_ui/': {
target: 'http://localhost:8080',
changeOrigin: true,
ws: true,
},
},
},[a-z0-9-]+ adalah charset slug workspace; ancor ^ menjaga _ui/api hanya cocok sebagai segmen pertama setelah slug — sama dengan predikat Go isWorkspaceAPIPath (cmd/formspec/dev.go) yang memegang kontrak yang sama ketika SPA disajikan lewat :8080 (--dev-ui). ws: true wajib untuk realtime: browser membuka /{ws}/_ui/_ws, dan tanpa itu upgrade WebSocket menggantung.
Jika backend di port berbeda, sesuaikan target.
Akses
Ganti default dengan slug workspace yang ingin dibuka (mis. kafe) — proxy meneruskan setiap slug, jadi tidak perlu mengubah vite.config.ts per workspace.
| URL | Keterangan |
|---|---|
http://localhost:5173/default/_admin | Admin panel — derived CRUD untuk semua entity |
http://localhost:5173/default/_admin/{module}/{plural} | List view entity |
http://localhost:5173/default/_admin/{module}/{plural}/new | Create form |
http://localhost:5173/default/_admin/{module}/{plural}/{id} | Detail page |
http://localhost:5173/default/_admin/{module}/{plural}/{id}/edit | Edit form |
http://localhost:5173/default/app | App surface (override UI kinds) |
3. Production Build
Untuk production, gunakan formspec serve --mode=production. Ia menegakkan batas produksi: Postgres wajib (DSN sqlite: ditolak), JWT wajib, CORS allow-list wajib (* ditolak), TLS opsional tapi --tls-cert/ --tls-key harus sepasang.
# Build SPA (opsional — lihat catatan sumber aset di bawah)
cd renderers/react-shadcn && npm run build && cd ..
formspec serve --mode=production \
--spec examples/Clinic-UI-Showcase/spec \
--dsn "postgres://formspec:secret@localhost:5432/clinic" \
--jwt-secret "…" # atau --jwt-public-key keys/jwt.pub (RS256/ES256)
--cors-origin https://clinic.example.com \
--tls-cert cert.pem --tls-key key.pem \
--metrics-addr 127.0.0.1:9102Aset SPA. serve menyajikan UI dari sumber pertama yang ada: --web-dir → renderers/react-shadcn/dist (auto-detect dari CWD) → cache formspec spa install → SPA yang ter-embed di binary. Jadi deployment dari checkout repo tidak butuh flag apa pun; deployment binary tunggal butuh --web-dir atau formspec spa install lebih dulu. Tanpa ketiganya, banner mencetak "no SPA assets — API only" (bukan 404 senyap).
formspec serve mengaktifkan:
- JWT auth — semua request API perlu token valid
- Strict
usesenforcement — action hanya bisa akses resource yang di-declare - Production logging — structured JSON-lines, tanpa debug output
- No auto-reload — spec dibaca sekali saat boot; tidak ada watcher
- Observability —
/metrics(Prometheus) +/healthdi--metrics-addr(default127.0.0.1:9102, hanya loopback; lewati--metrics-addr :9102bila memang ingin mengeksposnya)
Runtime non-Go. serve melayani action impl: {type: sidecar} bila diberi --sidecar-endpoint; tetapi listener ctx.* untuk app process non-Go dan spawning app child process hanya ada di formspec dev (todo ⏸️). Untuk sekarang runtime Go native dan Starlark berjalan penuh di serve.
Menambah workspace tanpa restart. Bila --admin-secret <token> di-set, listener admin menyajikan GET/POST /workspaces dan DELETE /workspaces/{slug}. Slug langsung routable pada request berikutnya — registri dibaca per request.
curl -H "Authorization: Bearer $ADMIN_SECRET" \
-d '{"slug":"cabang2","name":"Cabang 2"}' \
http://127.0.0.1:9102/workspaces
curl -H "Authorization: Bearer $ADMIN_SECRET" \
-X DELETE http://127.0.0.1:9102/workspaces/cabang2Tanpa --admin-secret, endpoint itu tidak di-mount sama sekali.
Untuk deployment skala besar, reverse proxy (nginx/caddy) tetap berguna untuk rate limiting, SSL termination, dan CDN — tetapi tidak wajib hanya untuk menyajikan dist/.
4. Reset Database
# Hapus file SQLite
rm .formspec/clinic.db
# Atau hapus seluruh state
rm -rf .formspecDatabase akan auto-generate saat engine restart.
5. Troublehshooting
Port already in use
Gunakan --force:
go run ./cmd/formspec/ dev ... --forceAtau manual:
# Cek proses yang pakai port
lsof -i :8080
lsof -i :5173
# Kill
kill <PID>
# Atau
kill -9 <PID>Blank page di browser
- Buka Chrome DevTools (F12) → Console, cek error
- Hard refresh (Ctrl+F5) — browser cache mungkin stale
- Pastikan Vite dan engine sama-sama running
- Cek proxy: akses langsung
http://localhost:8080/default/_ui/_meta/me
Table name with hyphens
Engine otomatis mengganti - dengan _ di nama tabel SQL. Jika masih error, pastikan engine versi terbaru (fix di internal/db/crud.go).
6. Contoh Lain
Billing (formerly Order-to-Cash)
go run ./cmd/formspec/ dev \
--spec verticals/billing/spec \
--dsn "sqlite:.formspec/billing.db" \
--addr :8080 \
--dev \
--forceReference App
go run ./cmd/formspec/ dev \
--spec examples/reference-app/spec \
--dsn "sqlite:.formspec/reference.db" \
--addr :8080 \
--dev \
--force7. Arsitektur Singkat
Browser ─── Vite (:5173) ─── proxy /default/api/v1/ → formspec dev (:8080)
│
┌─────────┴──────────┐
│ Entity Engine │
│ REST API │
│ Meta API │
│ ctx.* primitives │
│ App child process │
│ SQLite/Postgres │
└────────────────────┘- Vite dev server: HMR, hot reload, proxy API ke engine
formspec dev: Engine — entity engine, CRUD, permissions, Starlark, ctx.* primitives, spawn app child process- App child process: Business logic dalam bahasa apapun (Go/PHP/Python/Ruby/Java/.NET/TypeScript/Rust) via
lib-formspec-*SDK - SPA: React 19 + shadcn/ui — runtime reader Meta API, manifest-driven renderer