Admin UI
A built-in, no-build admin page for browsing and editing the data behind your auto-generated REST API. It is a single page — admin.html plus the admin.js ES module — that the browser loads directly: pure fetch + DOM, no bundler, no framework, no dependencies. It works against whatever collections you declare in backend/collections.toml; it discovers them at runtime.
Open it at /admin.
What it does
- Lists collections. On load it calls
GET /api/collectionsand renders the
collections in a sidebar. If none are declared, it shows a friendly empty state pointing you at the auto-API docs — nothing breaks.
- Browses records. Selecting a collection fetches
GET /api/collections/<name>/records and renders the records in a table, newest first. Columns are id, created, then one per declared field.
- Searches. The search box calls
?search=<q>(debounced) and re-renders. This
uses the same substring/semantic search the API exposes, so it follows whatever embedding configuration the server has.
- Creates records. The "New record" form is generated from the collection's
field schema — a text input for text, a number input for int/float, a checkbox for bool, with required fields marked. It POSTs JSON and refreshes the list. Validation errors (400 {"error": "..."}) are surfaced inline above the form.
- Edits records. Each row has an Edit button that turns the row into inline
inputs. Save computes only the changed fields and sends them as a PATCH; if nothing changed it just closes. Server validation errors appear under the row.
- Deletes records. Delete asks for confirmation, then sends
DELETEand
refreshes.
It is built only on the public API surface documented in Auto-generated REST API — there is no special admin endpoint.
Field types
The form and inline editors map each declared field type to an input:
| Field type | Input | Sent as | | ---------- | ----- | ------- | | text | text input | JSON string | | int | number input (step=1) | JSON number | | float | number input (step=any) | JSON number | | bool | checkbox | JSON boolean |
Empty non-boolean inputs are omitted from the request body, so the server's own required validation still applies and reports back inline.
Accessibility
Real <button> elements, labelled inputs, aria-current on the active collection, required fields marked with field-required, and an aria-live content region so screen readers announce when the view changes. It reuses the design system (.btn, .card, .table, .field, .badge, .empty, .search) with a small amount of admin-scoped CSS in the page.
Verifying it works
The demo site ships a posts collection (title text required, body text).
- Open
/admin. - The sidebar lists
posts. Click it. - You should see a "New record" form (Title + Body) and a records table.
- Create a record with a title; it appears at the top of the table.
- Try creating one with an empty title — the
400validation error shows inline. - Edit a row, change the title, Save — the table updates.
- Delete a row, confirm — it disappears.
The page logs breadcrumbs to the console you can check:
[admin] init
[admin] loaded 1 collections
[admin] selected collection posts
[admin] loaded N records for posts
[admin] created record id=…
[admin] updated record id=…
[admin] deleted record id=…