[data-reveal]{opacity:1!important;transform:none!important}
Components

Data controller

Declare a data shape and its CRUD operations once — route generators, field decorators, and model wiring.

A DataController is the backend data layer behind the DMS's data-driven components. It declares, once and with decorators, what each field of a record is and which operations exist — list, filter, sort, search, create/edit/delete, export, archive. From that single declaration the DMS generates the HTTP routes (each gated by its own permission), a TableView derives its columns and row actions (Tables), and the add/edit/view forms are built from the same fields (Forms).

DataController itself and the generic field decorators (@Listable, @Sortable, @Access, @Mandatory, …) are AntelopeJS concepts, not DMS ones — the framework's Data API guide and Database guide are their reference. The subject here is what the DMS layers on top, and how TableViews and forms derive from the controller.

Usage

DataController(tableClass, routes, baseController) is a factory: it returns a base class that your controller extends. The subclass needs three things:

  • the @RegisterDataController() decorator on the class;
  • one declare property per field — a field carries decorator metadata only, never a runtime value;
  • a model bound with @ModelReference() + @Model(...).
src/data-api/tasks.ts
import { Controller } from "@antelopejs/interface-api";
import { DataController, RegisterDataController } from "@antelopejs/interface-data-api";
import {
  Access,
  AccessMode,
  Listable,
  Mandatory,
  ModelReference,
  Sortable,
} from "@antelopejs/interface-data-api/metadata";
import { Model } from "@antelopejs/interface-database-decorators";
import { DefaultDataTypes } from "@antelopejs-private/cms/interfaces/cms-base/data-types";
import { Searchable } from "@antelopejs-private/cms/interfaces/cms-base/searchable";
import {
  ArchiveField,
  Column,
  Exported,
  Select,
  TableViewRoutes,
} from "@antelopejs-private/cms/interfaces/cms-base/table-view";
import { Task, TaskModel } from "../db/task";

@RegisterDataController()
export class taskDataAPI extends DataController(
  Task, // the @RegisterTable schema class
  TableViewRoutes.All, // full CRUD + export + archive/restore
  Controller("/api/task"), // the HTTP base path
) {
  @ModelReference()
  @Model(TaskModel) // bind the data model (an optional 2nd arg scopes it — see Multi-tenant data)
  declare model: TaskModel;

  @Listable()
  @Exported()
  @Access(AccessMode.ReadOnly)
  declare _id: string;

  @Searchable()
  @Select()
  @Listable()
  @Sortable()
  @Exported()
  @Column({ name: "Name", type: new DefaultDataTypes.StringType(), filterable: true })
  @Mandatory("new", "edit")
  @Access(AccessMode.ReadWrite)
  declare name: string;

  @Listable()
  @Exported()
  @Column({
    name: "Status",
    type: new DefaultDataTypes.SelectType({
      items: [
        { value: "pending", label: "Pending" },
        { value: "done", label: "Done" },
      ],
    }),
    filterable: true,
    defaultValue: "pending",
  })
  @Mandatory("new", "edit")
  @Access(AccessMode.ReadWrite)
  declare status: string;

  @ArchiveField()
  @Access(AccessMode.ReadOnly)
  declare isArchived: boolean;
}

To render this controller as a full CRUD screen, point a TableView at the class itself — see Tables.

Route generators (TableViewRoutes)

The second argument to DataController is a route map. TableViewRoutes.* wraps the generic data-api routes with the DMS's permission checks, validation, file handling, and realtime hooks; each route auto-registers its own permission.

RouteMethodPermissionPurpose
GetGET /getviewSingle row by id (also acquires edit presence).
ListGETlistPaginated, filtered, sorted list.
SelectGETselect (granted by default)Lightweight list for pickers/relations (@Select fields).
CountGETlistTotal count only.
NewPOSTaddCreate a row.
EditPUTeditUpdate a row.
DeleteDELETEdeleteDelete row(s).
Archive / RestorePUTarchive / restoreBulk soft-archive / restore by ids.
ExportRoutesGETexportexportStart / exportStatus / exportDownload.
All{ get, list, select, count, new, edit, delete, archive, restore, …ExportRoutes }.

Pass only the routes you want. A read-only controller passes { get, list, select, count } — no new/edit/delete, so a table on it renders no forms and those row actions disappear. The table's Export button appears only when all ExportRoutes keys are present.

Field decorators

A field's decorators decide which endpoints include it and how it behaves:

DecoratorFromMarks the field as…
@Column({ name, type, filterable?, group?, defaultValue?, readonlyBehavior?, isVisible?, order?, validate? })cms-baseA table column and a form field; type is a DataType instance. filterable: true wires per-column filtering; validation runs unless validate: false.
@Listable(requiredFields?)data-apiIncluded in the list result. For getter/joined fields, pass the DB fields they depend on, e.g. @Listable(["_instance"]).
@Select(requiredFields?)cms-baseIncluded in the select (picker) result — sugar for Listable(…, "select").
@Exported(requiredFields?)cms-baseIncluded in the export result — sugar for Listable(…, "export").
@Searchable(compareMode?)cms-basePart of the global search box (default mode contains). Distinct from filterable.
@Sortable({ noIndex? })data-apiSortable. Use { noIndex: true } for getter/joined fields without a DB index.
@Access(AccessMode.ReadOnly | ReadWrite | WriteOnly)data-apiRead/write capability; read-only fields drop out of edit forms.
@Mandatory("new", "edit")data-apiRequired in the listed form modes.
@ArchiveField()cms-baseThe boolean soft-archive flag; auto-applies Filter() + Listable().
@ColumnGroup(id, config) + group: "<id>"cms-baseGroup fields under a heading in the form.