Deploy a feature flag service on Val.town powered by nano-supabase with persistent storage.
dumpDataDir/loadDataDir (data survives cold starts).ts files (index.ts, persistence.ts, schema.ts) into the editorhttps://YOUR_USERNAME-YOUR_VAL_NAME.web.val.runnpm install -g @valtown/vt vt login cd examples/valtown vt create my-flags . --no-editor-files --upload-if-exists --public # Set the val type to HTTP on the web page vt browse
| Method | Path | Description |
|---|---|---|
| GET | / | API info and available endpoints |
| GET | /flags | List all flags (?enabled, ?app, ?environment) |
| POST | /flags | Create flag { name, description?, enabled?, rollout_percentage? } |
| GET | /flags/:name | Get flag details with environments and apps |
| PATCH | /flags/:name | Update flag { description?, enabled?, rollout_percentage? } |
| DELETE | /flags/:name | Delete flag (cascades to environments and apps) |
| POST | /flags/:name/toggle | Toggle flag enabled/disabled |
| GET | /flags/:name/evaluate | Evaluate flag ?app=x&environment=y&identifier=z |
| POST | /flags/:name/environments | Add environment override { environment, enabled } |
| POST | /flags/:name/apps | Add app scope { app_name } |
./test.sh https://YOUR_USERNAME-YOUR_VAL_NAME.web.val.run
Or test manually:
# Create a flag with 50% rollout curl -X POST https://YOUR_URL/flags \ -H "Content-Type: application/json" \ -d '{"name": "dark-mode", "enabled": true, "rollout_percentage": 50}' # Add a production override (disabled) curl -X POST https://YOUR_URL/flags/dark-mode/environments \ -H "Content-Type: application/json" \ -d '{"environment": "production", "enabled": false}' # Scope to a specific app curl -X POST https://YOUR_URL/flags/dark-mode/apps \ -H "Content-Type: application/json" \ -d '{"app_name": "web-app"}' # Evaluate: is this flag active for web-app in staging? curl "https://YOUR_URL/flags/dark-mode/evaluate?app=web-app&environment=staging&identifier=user-123" # Toggle a flag curl -X POST https://YOUR_URL/flags/dark-mode/toggle
When evaluating a flag (GET /flags/:name/evaluate?app=x&environment=y):
active: false (flag_not_found)active: false (app_not_scoped)enabled value takes precedence over the global flag enabledenabled is false -> active: false (disabled)rollout_percentage < 100, a deterministic hash of the identifier param decides inclusion. Without an identifier, it uses random selection -> active: false (rollout_excluded)active: trueThe database is persisted using PGlite's built-in dumpDataDir/loadDataDir. On each write operation, the entire database is serialized as a gzipped tarball and stored in Val.town's blob storage. On cold start, the snapshot is loaded back, restoring all tables, indexes, and data atomically.
The blob key is nano-supabase-flags-db.
index.ts - HTTP handler with all feature flag endpointspersistence.ts - Database initialization and snapshot persistenceschema.ts - Table definitions (feature_flags, flag_environments, flag_apps)"Not found" response: The val type isn't set to HTTP. Go to the val's page and change the type dropdown to "HTTP".
"Invalid version provided" error: Try deleting the val and creating a new one with a different name, or clear Val.town's module cache by changing the import URL (add ?v=2).
Import errors: Make sure you're using the exact versions specified in the import statements.