Introduction
Overview
The root export of @antelopejs/interface-dms and the subpaths listed below are the core contract of the AntelopeJS DMS. It encodes one inversion: the backend declares the screen, as serializable data. A page is a controller class, its components are static fields holding builders, and the page's layout route returns the description a shared frontend renders. Adding an admin screen is a backend change.
Around that core the interface carries what an admin backend needs to be more than a renderer: hierarchical permissions derived from the page tree, multi-tenant data access scoped by the model handle, lifecycle hooks, realtime topics, and command-palette entries.
Everything here is imported from a subpath of @antelopejs/interface-dms. Two other domains of the same package complete the picture and are referenced throughout these pages: Auth (/auth) for authentication and the User table, and Base (/base) for the concrete component vocabulary (Form, TableView, Grid, the chart family) built on the component types documented here.
Key Features
- Pages as controllers —
PageControllerbuilds a controller class bound to a URL;@RegisterPage()records it, registers its permissions, and serves its layout. - Components that serialize — a
ComponentBuilderproduces data, not a live component, so one frontend renders every module's screens. - Categories and modules — a sidebar tree of nested categories, plus modules that own a whole branch under
/modules. - Dynamic navigation — providers that resolve extra menu entries per request, so one page can back an entry per project or workspace.
- Page extensions — inject components into a page owned by another module, permissioned in that page's own tree.
- Hierarchical permissions — ids derived from page, component and action, with a base set from roles and an effective set shaped by registered resolvers.
- Route guards — permission, tenant-member and tenant-owner decorators usable as parameter or property decorators.
- Tenancy in the handle — per-tenant schemas and model handles bound to one tenant, rather than a tenant column filtered on every query.
- Tenant access gates — one binary check that denies a tenant at every authorization surface at once.
- Invites — one call that either grants membership to an existing user or issues a token-bearing invite.
- Lifecycle hooks — typed events for tenants, members, invites and registrations, dispatched either to notify or to collect contributions.
- Realtime topics — bind a topic to a page, publish to whoever is on it, and subscribe from the backend to what others publish.
- Quick actions — contribute grouped, permission-gated entries to the command palette.
Dependencies
This interface relies on the following AntelopeJS packages:
- @antelopejs/interface-api - Controllers, route registration and request context.
- @antelopejs/interface-auth - The authentication decorator machinery the guards build on.
- @antelopejs/interface-core - Interface functions, registering proxies and decorator helpers.
- @antelopejs/interface-database-decorators - Table definitions, data models and model injection.
- @antelopejs/interface-data-api - Used by the
/data-controllerssubpath for the built-in members and roles controllers.
Quick Start
Declare the application's table and model first. BookModel is not a DMS export; this example defines it with the stock flag used by the KPI query.
import {
BasicDataModel,
Field,
RegisterTable,
Table,
} from "@antelopejs/interface-database-decorators";
export const CATALOG_SCHEMA = "example_catalog";
@RegisterTable("books", CATALOG_SCHEMA)
export class Book extends Table {
@Field("string") declare _id: string;
@Field("string") declare title: string;
@Field("boolean") declare inStock: boolean;
}
export class BookModel extends BasicDataModel(Book, "books") {}
Then declare a category, a page with a component, and the route that component fetches from:
import { Get } from "@antelopejs/interface-api";
import { GetModel } from "@antelopejs/interface-database-decorators";
import {
Category,
PageController,
RegisterPage,
pagesCategory,
} from "@antelopejs/interface-dms/page";
import { KpiCard } from "@antelopejs/interface-dms/base";
import { BookModel } from "./db";
export const catalog = Category("catalog", {
displayName: "Catalog",
icon: "i-ph-books",
category: pagesCategory,
});
@RegisterPage()
export class BooksPage extends PageController("books", {
displayName: "Books",
description: "Everything currently in the catalog",
icon: "i-ph-book-open",
category: catalog,
order: 1,
}) {
static inStockKpi = KpiCard({
title: "In stock",
icon: "i-ph-package",
fetchUrl: "/catalog/books/stats/in-stock",
valueFormat: "number",
});
@Get("/stats/in-stock")
async inStock() {
const value = await GetModel(BookModel).table
.filter((book) => book.key("inStock").eq(true)).count().run();
return { value };
}
}
Import the page and register the schema from your module's entry file, merging this into an existing start() if necessary:
import "./books-page";
import { RegisterSchema } from "@antelopejs/interface-database-decorators";
import { CATALOG_SCHEMA } from "./db";
export async function start(): Promise<void> {
await RegisterSchema(CATALOG_SCHEMA);
}
The page resolves at /catalog/books and its layout at /dms/pages/catalog/books/pagelayout: a page controller's routes live under /dms/pages (pageRoutePath), a prefix the frontend server proxies to the backend, since the page's own path is the page. Its permission id is the chain of ids down from the root category — pages.catalog.books — with pages.catalog.books.inStockKpi registered underneath it.
Subpath Map
| Subpath | Contains | Documented in |
|---|---|---|
/page | Pages, categories, modules, layouts, page extensions, dynamic menus, frontend modules | Pages and Modules |
/component | Component, ComponentBuilder, actions, watches | Components |
/component-slots | RegisterComponentSlot, ComponentSlotResolver, SlotOptions | Components |
/attachments | SaveComponentFiles and its request, field and result types | Pages and Modules |
/permission-gate | Shared PermissionGate fields | Base types |
/permissions | The permission registry and the permission computation | Permissions |
/permissions-resolver | Registering and running permission resolvers | Permissions |
/guards | AuthUserWithPermission, AuthTenantOwner, AuthTenantMember | Permissions |
/constants | Schema names and the default tenant | Tenancy |
/db | The Tenant, TenantMember, Role and UserInvite tables and models | Tenancy, Invites |
/request-tenant | getRequestTenantId | Tenancy |
/tenant-scoped-model | TenantScopedModel | Tenancy |
/tenant-access | Tenant access gates | Tenancy |
/tenant-ownership | Membership and ownership writes | Tenancy |
/tenant-lifecycle | Producer admission and closure before tenant deletion | Tenancy |
/data-controllers | The built-in members and roles data controllers | Tenancy |
/invites | Inviting a user into a tenant | Invites |
/invite-extensions | Invite form contributions, callbacks and availability | Invites |
/invite-resolution | Durable terminal decisions and retryable effects | Invites |
/hooks | The Hook enum, handlers and dispatch | Hooks |
/tenant-export | The tenant data export archive, contribution and manifest types, re-exported from /hooks | Hooks |
/realtime | Page topics, publishing and backend subscription | Realtime |
/quick-actions | Command-palette entries | Quick Actions |
/layout-banners | Global banners under the dashboard header | Layout Banners |
/uploads | Signed upload tokens for the file and image fields of a serialized component | Pages and Modules |
/types | MaybePromise, the one shared type alias | Pages and Modules |
/api-base-url, /client-base-url | GetApiBaseUrl, GetClientBaseUrl: the public origins of the API and the dashboard | Pages and Modules |
/client-ip | getClientIp, ResolveClientIp: a request's client address behind the trusted proxies | Pages and Modules |
Paths under internal/, symbols tagged @internal, and internal namespaces are implementation wiring, not supported consumer APIs. The wildcard export does not change that boundary.
Documentation Sections
- Pages and Modules - Declare pages, group them into categories, own a branch of the sidebar with a module, and extend a page from another one
- Components - Build serializable components, declare actions and watches
- Permissions - Register permissions, compute a user's effective set, guard routes
- Tenancy - Schemas, the tenancy tables, tenant-scoped models and access gates
- Invites - Bring a user into a tenant by email
- Hooks - React to platform events and contribute to the tenant data export
- Realtime - Bind topics to pages, publish messages and subscribe to them
- Quick Actions - Contribute entries to the command palette
- Layout Banners - Show a global banner under the dashboard header, decided per request