[data-reveal]{opacity:1!important;transform:none!important}
Resources
Interface reference

Resources

Overview

A resource is the data half of a DMS feature: a database Table, a DataController exposing it over HTTP, and the model that binds them. CreateResource writes all of it into a <name>/ folder — database.ts, data-api.ts and an index.ts barrel — and wires the nearest barrel above so the project keeps a relative import chain to src/index.ts.

You describe a resource in semantic terms: each field carries a DataType and a set of aspects such as searchable or sortable, and the engine maps those to the paired decorator stacks on the table and the controller. You never write a decorator yourself, and you never name one in a request.

A resource is also what a controller-leading block points at: AddBlock with controller: "<ref>" emits the resource's DataAPI class as the block's leading argument, as described in Page Operations.

Address a Resource and a Field

ResourceRef is the resource's name in kebab form — "product", "order-line". It is what CreateResource returns, what ListResources reports, and what every other resource operation takes.

FieldPath addresses one field within a resource: <resourceRef>#<fieldName>, e.g. product#price. AddField returns the path it created.

import type {
  FieldPath,
  ResourceRef,
} from "@antelopejs/interface-dms-builder";

const resource: ResourceRef = "product";
const field: FieldPath = "product#price";

Create a Resource

import { CreateResource } from "@antelopejs/interface-dms-builder";

const result = await CreateResource({
  name: "product",
  displayName: "Product",
  fields: [
    {
      name: "title",
      dataType: { $dataType: "string" },
      label: "Title",
      required: true,
      searchable: true,
      sortable: true,
    },
    {
      name: "price",
      dataType: { $dataType: "number" },
      sortable: true,
      filterable: true,
    },
  ],
});
if (result.ok) {
  console.log(result.data.ref, result.data.files.dataApi);
}

CreateResourceInput:

OptionTypeDescription
namestringThe resource name. Required, and it must contain at least one alphanumeric character. A duplicate fails with duplicate_name.
fieldsFieldSpec[]The resource's fields. Required.
displayNamestringA human-readable name for the resource.
schemastringThe database schema. Defaults to the DMS core schema, CORE_SCHEMA_NAME (dms-core).
seedsRecord<string, unknown>[]Rows to seed as a fixture. Each row must supply its own _id.
locationResourceLocationWhere the generated folder goes. Omit for src/<name>/.
routesResourceRoute[]Which DataAPI routes to expose. Omit for the full set. Must be non-empty when provided.

The names the engine derives from name are predictable: product yields a Product table class, a ProductModel, a productDataAPI controller on the route /api/product, and a products table.

Describe a Field

FieldSpec is a FieldAspects plus the field's name. The name is immutable — renaming a field is RemoveField followed by AddField.

OptionTypeDescription
namestringThe field name. Rejected with invalid_config when it is one of the catalog's reservedFieldNames.
dataTypeDataTypeValueThe field's DataType, e.g. { $dataType: "string" }. Required.
labelstringColumn header label. Defaults to the field name.
listablebooleanSelected and listed by the DataAPI. Default true.
searchablebooleanIncluded in full-text search.
sortablebooleanSortable in the table view.
filterablebooleanColumn-filterable in the table view.
access"read" | "readwrite"Field access mode. Default "readwrite".
requiredbooleanWhether the field is required. Default false, which makes the field optional.
indexedbooleanWhether the table column is indexed.
ordernumberDisplay order of the column.

dataType takes a DataTypeValue sentinel, not a bare string; the ids come from the catalog's dataTypes. See Value Encodings and Catalog.

Choose the Exposed Routes

ResourceRoute names one group of DataAPI endpoints. Passing a subset lets a resource serve only what it needs — a singleton edit form wants ["get", "edit"] and nothing else. An omitted route's HTTP endpoint is simply not registered.

RouteWhat it exposes
listThe paginated listing (list + count).
getRead a single row.
createCreate a row.
editUpdate a row.
deleteDelete a row.
selectOption lists for relation pickers that reference this resource.
archiveSoft delete and undo (archive + restore).
exportThe export endpoints (exportStart / exportStatus / exportDownload).
import { CreateResource } from "@antelopejs/interface-dms-builder";

await CreateResource({
  name: "site-settings",
  fields: [{ name: "title", dataType: { $dataType: "string" } }],
  routes: ["get", "edit"],
});

An empty array, or a name that is not one of the eight, fails with invalid_config.

Place the Generated Files

ResourceLocation decides which folder the <name>/ folder lands in. The nearest ancestor barrel to the resolved folder is the one that gets wired, with the root barrel as the fallback.

FormWhere the resource lands
omittedsrc/<name>/
"reporting/data"src/reporting/data/<name>/ — an explicit folder relative to the src root, which must stay inside the src tree.
{ category: "pages.reports" }The source folder of that category, mirroring page placement. A root category falls back to the src root.
{ page: "/revenue" }Inside that page's feature folder, so the page imports ./<name>/data-api.
import { CreateResource } from "@antelopejs/interface-dms-builder";

await CreateResource({
  name: "revenue-entry",
  fields: [{ name: "amount", dataType: { $dataType: "number" } }],
  location: { page: "/revenue" },
});

A dir string that escapes the src tree, an unknown category and an unknown page all fail with invalid_config.

Add, Configure and Remove a Field

AddField(ref, field, opts?) appends a field to both generated files and returns its FieldPath.

import { AddField } from "@antelopejs/interface-dms-builder";

const result = await AddField("product", {
  name: "sku",
  dataType: { $dataType: "string" },
  label: "SKU",
  indexed: true,
  searchable: true,
});
if (result.ok) {
  console.log(result.data.path); // "product#sku"
}

A name already present on the table or the controller fails with duplicate_name; a reserved name fails with invalid_config.

ConfigureField(path, patch, opts?) reads the field's current aspects, merges your Partial<FieldAspects> over them, and rewrites both declarations from the result. Aspects you leave out keep their current value.

import { ConfigureField } from "@antelopejs/interface-dms-builder";

await ConfigureField("product#price", {
  filterable: true,
  order: 2,
});

A field whose decorator stack the builder could not reverse — a hand-edited one, or one carrying a decorator the builder does not know — is opaque, and configuring it fails with opaque_target.

RemoveField(path, opts?) drops the field from both files.

import { RemoveField } from "@antelopejs/interface-dms-builder";

await RemoveField("product#sku");

All three take a MutationOpts, whose expectedVersion is checked against the resource's version — a hash of both generated files.

List Resources

ListResources() returns a ResourceSummary[] directly, without an OpResult wrapper.

FieldTypeDescription
refResourceRefThe resource's ref.
classNamestringThe generated table class name.
tableNamestringThe database table name.
routestringThe DataAPI's base route.
fieldCountnumberHow many fields the resource has.
files{ database: string; dataApi: string }The two source files.
import { ListResources } from "@antelopejs/interface-dms-builder";

const resources = await ListResources();
const refs = resources.map((resource) => resource.ref);

Read a Resource's Structure

GetResourceStructure(ref) returns an OpResult<ResourceStructure>. Like the other reads, its changes array is empty.

import { GetResourceStructure } from "@antelopejs/interface-dms-builder";

const result = await GetResourceStructure("product");
if (result.ok) {
  const editable = result.data.fields.filter((field) => !field.opaque);
}

ResourceStructure:

FieldTypeDescription
refResourceRefThe resource's ref.
classNamestringThe generated table class name.
tableNamestringThe database table name.
routestringThe DataAPI's base route.
schemastringThe database schema the table is registered in.
filesResourceFilesThe resource's three files.
fieldsResourceFieldStructure[]The fields, in declaration order.
routesResourceRoute[]The exposed routes when the resource serves a subset; omitted when it serves the full set. Best-effort: absent for a hand-edited route map the engine could not map back to semantic names.
versionstringA content hash of both generated files.

ResourceFiles names the three paths a resource is composed of: database, dataApi and index.

ResourceFieldStructure is a Partial<FieldAspects> plus:

FieldTypeDescription
namestringThe field name. Always present.
opaquebooleantrue when the field's decorator stack could not be reversed.
opaqueReasonstringWhy it is opaque, e.g. an unsupported decorator or a missing @Column stack. Set when opaque === true.

A cleanly-reversed field carries its aspects; an opaque one carries only name and opaqueReason. Read opaque before offering a field for editing.

Delete a Resource

DeleteResource(ref, opts?) deletes the three generated files, unwires every import of them, removes the folder if it is now empty, and — by default — clears the resource's database table so a later resource reusing the name re-seeds cleanly instead of inheriting stale documents.

import { DeleteResource } from "@antelopejs/interface-dms-builder";

const result = await DeleteResource("product");
if (result.ok) {
  for (const warning of result.warnings ?? []) {
    console.log(warning.code, warning.message);
  }
}

DeleteResourceOpts extends MutationOpts:

OptionTypeDescription
keepDatabooleanLeave the resource's table data in place instead of deleting every row.
expectedVersionstringThe ResourceStructure.version you last read. A mismatch fails with stale.

When the rows cannot be dropped, the operation still succeeds and attaches a data_not_dropped warning naming the reason — the source is gone, the data may not be.

import { DeleteResource } from "@antelopejs/interface-dms-builder";

await DeleteResource("product", { keepData: true });

Next Steps

  • Queries - Derive counts and aggregates from a resource and serve them from a page