Extending
The builder UI is one client of the builder engine. Other modules (the AI assistant, your own tooling) drive the same operations through the interface or the HTTP API.
The interface
@antelopejs/interface-dms-builder exposes every operation as an InterfaceFunction: import it and call it like any async function from a module in the same process.
import { AddBlock } from "@antelopejs/interface-dms-builder";
const result = await AddBlock({
page: "/revenue",
name: "total",
type: "KpiCard",
config: { title: "Total revenue", icon: "i-ph-currency-eur" },
});
if (result.ok) {
console.log(result.data.path); // "/revenue#total"
}
Mutations return an OpResult: { ok: true, data, changes } with the files changed, or { ok: false, error }. Its reference covers:
| Topic | Read |
|---|---|
| Addressing pages, blocks, resources and queries | Addressing and structure |
| The block and DataType catalog | Catalog |
| Creating and editing pages | Page operations |
| Tables with their API | Resources |
| Data sources | Queries, A chart without code |
Drafts, preview and expectedVersion | Drafts and preview |
| Errors | Results and errors |
The HTTP API
All routes sit under /api/builder and require an authenticated tenant owner. Operations answer with the interface's OpResult, so a client branches on ok rather than on the status code. The reads that only describe the project (/tables/usage, /page/impact, /page/access, /permissions) answer with their value.
| Method | Path | Body / query | Operation |
|---|---|---|---|
GET | /status | Whether the API is live, and the project root it edits | |
GET | /catalog | GetCatalog | |
GET / POST | /pages | CreatePageInput | ListPages / CreatePage |
GET | /categories | ListCategories | |
GET / DELETE | /page | ?ref= | GetPageStructure / DeletePage |
POST | /page/configure | { page, patch, expectedVersion? } | ConfigurePage (an option sent as null is removed) |
POST | /page/preview | { page, draft } | PreviewLayout |
POST | /page/blocks | { page, draft, expectedVersion? } | SetPageBlocks |
GET | /page/impact | ?ref= | What deleting the page takes |
GET | /page/access | ?ref=&permission= | Who reaches the page and each of its blocks |
GET | /permissions | The permissions the DMS registered | |
GET / POST | /resources | CreateResourceInput | ListResources / CreateResource |
GET | /resource | ?ref= | GetResourceStructure |
POST | /resource/configure | { resource, patch, expectedVersion? } | ConfigureResource (the routes a table serves) |
POST / PUT / DELETE | /resource/fields | field input / { path, patch } / ?path= | AddField / ConfigureField / RemoveField |
GET | /tables/usage | The pages reading each table, and its rows | |
GET | /query-templates | ListQueryTemplates | |
POST / PUT / DELETE | /queries | { page, input } / { query, patch } / ?ref= | AddQuery / ConfigureQuery / RemoveQuery |
POST | /refresh | RefreshSourceIndex |
Edit a whole page
An editor holds a draft rather than replaying edits: read the structure, edit locally, preview as often as needed, and write once.
Read
GET /api/builder/page?ref=/dashboard returns the PageStructure, with its version.
Preview
POST /api/builder/page/preview returns the serialized layout of the draft.
Write
POST /api/builder/page/blocks with expectedVersion writes the draft in one transaction and one typecheck. If the page changed underneath, it fails with stale instead of overwriting.
Make your blocks editable
The builder reads blocks back by shape: a hand-written declaration that matches the grammar of a DMS factory call is as editable as a generated one; one that drifts out of it is shown as opaque rather than rewritten. Custom blocks registered with RegisterBlockType appear in the catalog with a panel generated from their schema; give them .meta({ name, description, icon }) so the builder and the role editor name them.
Frontend module
frontend-vue/dms.frontend.ts registers the DmsBuilder components and the client plugin at priority 100, only when the backend's manifest entry carries dmsBuilder.enabled === true.