Skip to content

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, atau go install untuk Go developer.

OpsiCaraTerminalHMRCocok untuk
A — --dev-uiformspec dev spawn Vite otomatis1✅Development paling praktis
B — Manualformspec dev + npm run dev2✅Development
C — Staticformspec dev + --web-dir1❌Demo / produksi

Opsi A: Satu Terminal — --dev-ui ​

bash
go run ./cmd/formspec/ dev \
  --spec examples/Clinic-UI-Showcase/spec \
  --dsn "sqlite:.formspec/clinic.db" \
  --addr :8080 \
  --force --dev-ui

formspec dev akan:

  1. Kill engine sebelumnya (kalau ada) berkat --force
  2. Load engine + REST API di :8080
  3. Spawn npm run dev sebagai child process — Vite HMR siap di :5173
  4. Saat Ctrl+C, Vite ikut dimatikan

Buka http://localhost:5173/default/\_admin.

Opsi B: Dua Terminal ​

Terminal 1 — Engine:

bash
go run ./cmd/formspec/ dev \
  --spec examples/Clinic-UI-Showcase/spec \
  --dsn "sqlite:.formspec/clinic.db" \
  --addr :8080 \
  --dev --force

Terminal 2 — Frontend (Vite HMR):

bash
cd renderers/react-shadcn && npm run dev

Opsi C: Static SPA (tanpa Vite) ​

bash
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/dist

Satu port :8080 untuk API + SPA. Buka http://localhost:8080/default/\_admin.

Setiap edit frontend perlu npm run build ulang.

Flags ​

FlagFungsiDefault
--specPath ke direktori YAML manifests./spec
--dsnDatabase DSN (sqlite atau postgres)sqlite:.formspec/data.db
--addrREST API listen address:8080
--devDev mode (hot-reload, unsigned artifacts) — auth tetap JWT asli, seragam dengan prodfalse
--jwt-secretHMAC secret untuk JWT signing. Kosong di dev → auto-generate + persist ke .formspec/dev-jwt-secret; kosong di prod → errorauto
--state-dirLocal state directory.formspec
--forceKill previous formspec engine on same portsfalse
--web-dirBuilt SPA directory (e.g. renderers/react-shadcn/dist)""
--dev-uiSpawn npm run dev otomatis (implikasikan --dev)false
--runtimeApp runtime: auto, go, php, python, ruby, java, dotnet, rust, nodeauto
--app-dirApp source directory (child-process runtime).formspec/app
--app-entrypointEntrypoint file (default tergantung runtime)auto

--listen dan --app-endpoint sudah otomatis diatur oleh formspec dev — tidak perlu di-set manual.

Reset Database ​

bash
rm -rf .formspec

Database auto-generate saat engine restart.

Troubleshooting ​

MasalahSolusi
Port already in useTambah --force
Blank pageHard refresh (Ctrl+F5)
Permission deniedformspec dev pilih socket/HTTP otomatis berdasarkan environment
Hyphen di tabel SQLEngine otomatis - → _ (fix di internal/db/crud.go)

Prasyarat ​

ToolMinimalCek
Go1.26+go version
Node.js22+node --version
npm10+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-dir dan spawn app child process
  • Enforce permission, tenant isolation, ctx.* primitives

Command ​

bash
# Dari root repository
cd /workspaces/formspec

go run ./cmd/formspec/ dev \
  --spec examples/Clinic-UI-Showcase/spec \
  --dsn "sqlite:.formspec/clinic.db" \
  --addr :8080 \
  --dev

App 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:

FileRuntime
go.modGo
Cargo.tomlRust
package.jsonNode.js / TypeScript
composer.jsonPHP
pyproject.toml / requirements.txtPython
GemfileRuby
pom.xml / build.gradleJava
*.csproj / *.sln.NET

Override manual dengan --runtime <name> atau runtime: di formspec-app.yaml.

Flags ​

FlagFungsiDefault
--specPath ke direktori YAML manifests./spec
--dsnDatabase DSN (sqlite atau postgres)sqlite:.formspec/data.db
--addrREST API listen address:8080
--devDev mode (auth bypass, unsigned artifacts)false
--state-dirLocal state directory.formspec
--forceKill previous formspec engine on same portsfalse
--web-dirBuilt SPA directory""
--dev-uiAuto-spawn Vite dev serverfalse
--runtimeOverride runtime auto-detectauto
--app-dirApp source directory.formspec/app
--app-entrypointEntrypoint fileauto

--listen dan --app-endpoint sudah diatur internal — tidak perlu di-set manual. --app-endpoint-url untuk 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:

bash
port 8080 is already in use by "nginx" (PID 12345).
Use --force to kill a previous formspec instance, or stop the other program manually

Contoh: Clinic UI Showcase ​

bash
mkdir -p .formspec
go run ./cmd/formspec/ dev \
  --spec examples/Clinic-UI-Showcase/spec \
  --dsn "sqlite:.formspec/clinic.db" \
  --addr :8080 \
  --dev \
  --force

Output:

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 :8080

Verifikasi:

bash
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 ​

bash
cd renderers/react-shadcn

# Install dependencies (pertama kali)
npm install

# Jalankan dev server
npm run dev

Vite Proxy ​

Vite perlu proxy API calls ke backend. Konfigurasi ada di renderers/react-shadcn/vite.config.ts:

typescript
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.

URLKeterangan
http://localhost:5173/default/_adminAdmin panel — derived CRUD untuk semua entity
http://localhost:5173/default/_admin/{module}/{plural}List view entity
http://localhost:5173/default/_admin/{module}/{plural}/newCreate form
http://localhost:5173/default/_admin/{module}/{plural}/{id}Detail page
http://localhost:5173/default/_admin/{module}/{plural}/{id}/editEdit form
http://localhost:5173/default/appApp 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.

bash
# 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:9102

Aset 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 uses enforcement — 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) + /health di --metrics-addr (default 127.0.0.1:9102, hanya loopback; lewati --metrics-addr :9102 bila 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.

bash
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/cabang2

Tanpa --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 ​

bash
# Hapus file SQLite
rm .formspec/clinic.db

# Atau hapus seluruh state
rm -rf .formspec

Database akan auto-generate saat engine restart.

5. Troublehshooting ​

Port already in use ​

Gunakan --force:

bash
go run ./cmd/formspec/ dev ... --force

Atau manual:

bash
# Cek proses yang pakai port
lsof -i :8080
lsof -i :5173

# Kill
kill <PID>
# Atau
kill -9 <PID>

Blank page di browser ​

  1. Buka Chrome DevTools (F12) → Console, cek error
  2. Hard refresh (Ctrl+F5) — browser cache mungkin stale
  3. Pastikan Vite dan engine sama-sama running
  4. 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) ​

bash
go run ./cmd/formspec/ dev \
  --spec verticals/billing/spec \
  --dsn "sqlite:.formspec/billing.db" \
  --addr :8080 \
  --dev \
  --force

Reference App ​

bash
go run ./cmd/formspec/ dev \
  --spec examples/reference-app/spec \
  --dsn "sqlite:.formspec/reference.db" \
  --addr :8080 \
  --dev \
  --force

7. 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

Standar terbuka (CC0) dengan implementasi referensi.