routes

The router does one thing: binds a path to what is shown there. Data and write logic live in the tables (views and forms); a route only references them by name. A page route with path "/" (the site index) is required.

page — an HTML page: path, hbs (the template file, with the .hbs extension), optional view for data. Which skeleton wraps the page is written in the template itself — it opens with {{#> _layout}} (see Templates).

A multi-block page (a homepage, a landing) declares views instead of view — several named views on one page, executed concurrently: "views": { "fresh": "home_items", "faq": "faq_list" }. Each key holds its result in the template context — {{#each fresh.rows}}, {{faq.total}}, for a single view {{key.row.title}}. The two keys cannot be mixed on one route, and params is reserved.

api, GET — serves view data: path, view, format (json | html). Always raw values.

api, method POST — the ADDRESS of a public form: { "method": "POST", "path": "/api/feedback", "form": "contact" }. The form itself is declared in its table (tables.<t>.forms):

form keydescription
fieldswhat a client may submit: name, widget, required, max_length
accessadmin (default) | user | public. Only a public form is accepted at a public address — the engine checks the entry-point status at runtime, the config cannot bypass it
honeypotbot-trap field name; a non-empty value means silent success, nothing stored
auto_fieldsserver-side values: column → "now" (the intake timestamp)
on_success / on_errorredirect to a path, or render an hbs template

Intake is rate-limited by the engine itself — there is no config switch to weaken it.

Real wiring from this site — the page, the JSON endpoint, the contact form address and the form it points to:

"routes": [
  { "type": "page", "path": "/docs/{grp}", "hbs": "docs/group.hbs", "view": "doc_list" },
  { "type": "api",  "path": "/api/docs",  "view": "doc_list",  "format": "json" },
  { "type": "api",  "method": "POST", "path": "/api/feedback", "form": "contact" }
]

"tables": { "feedback": { "forms": {
  "contact": {
    "access": "public",
    "fields": [ { "name": "message", "required": true, "max_length": 5000 } ],
    "honeypot": "website",
    "auto_fields": { "created_at": "now" },
    "on_success": { "redirect": "/thanks" },
    "on_error": { "hbs": "errors/400.hbs" }
  }
} } }

Paths on or under /cms, /mcp and /static/wasm are reserved by the engine (the admin UI, the MCP agent door, the WASM runtime).

← All articles in this group