Documentation

Collections and schemas

Built-in vs custom collections, the schema shape, and how to define your own at runtime.

A collection is a typed table — backed by MongoDB under the hood, but exposed through AppEngine's CRUD layer rather than directly. AppEngine ships ~150 built-in collections (contacts, products, orders, pages, leads, …) and lets you define your own without redeploying.

Built-in collections (selected)

The full set lives in @jaclight/dbsdk's DataType enum and is loaded at boot. The ones you'll touch most:

  • Storefrontproduct, productvariant, category, cart, order, lineitem, discount, giftcard, inventory, shipping, tax, invoice, subscription, rental, return
  • CRMcontact, lead, audience, campaign, crm-message, crm-ticket, crm-reservation, task, opportunity, note, activity, crm-customer
  • Site / CMSpage, site, domain, theme, category, tag, image, video, file
  • Authuser, customer, userrole, apikey, device, organization
  • Automationautomation, flow, step, trigger, action, ruleset, rule
  • Bankingaccount, transfer, card, kyc, loan, journal-entry
  • Communitycommunity-page, post, story, follow, bookmark, badge

Built-in collections come with hand-tuned JSON schemas, validation rules, indexes, and event hooks. Custom collections share the same CRUD endpoints but you supply the schema.

Schema shape

A collection record is itself a BaseModel<CollectionModel>. The interesting bit is data:

{
  name: 'product',                    // matches datatype
  title: 'Products',
  description: 'Catalogue of items for sale',
  schema: {
    type: 'object',
    properties: {
      sku: { type: 'string' },
      title: { type: 'string' },
      price: { type: 'number' },
      // ...JSON Schema with x- extensions for UI/validation
    },
    required: ['sku', 'title'],
  },
  indexes: [{ fields: ['sku'], unique: true }],
  enableWorkflow: false,              // toggles the approval pipeline
  enableHistory: true,
  requiredRole: { read: [...], create: [...], update: [...], delete: [...] },
  events: { onCreate: '...', onUpdate: '...' },
}

The schema is JSON Schema with AppMint extensions:

  • x-control / x-control-variant — which form control to render
  • x-render — output renderer (e.g. file, html, markdown)
  • dataSource — populate selects from another collection or function
  • hidden, hideIn, readOnly — UI behavior

The same schema is used to validate API writes, generate admin forms, and drive the Vibe Studio low-code builder.

Defining a custom collection

Create the collection record itself with a POST /repository/create, with datatype: 'collection'.

POST/repository/createJWT
curl -X POST https://appengine.appmint.io/repository/create \
  -H "orgid: my-org" \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "datatype": "collection",
    "data": {
      "name": "vehicle",
      "title": "Vehicles",
      "schema": {
        "type": "object",
        "properties": {
          "vin":   { "type": "string" },
          "make":  { "type": "string" },
          "model": { "type": "string" },
          "year":  { "type": "integer" }
        },
        "required": ["vin", "make", "model"]
      },
      "indexes": [{ "fields": ["vin"], "unique": true }]
    }
  }'

Once created, the new collection is immediately usable through the same CRUD surface as any built-in:

# write a vehicle
curl -X POST https://appengine.appmint.io/repository/create \
  -H "orgid: my-org" \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{ "datatype": "vehicle", "data": { "vin": "1HGBH...", "make": "Honda", "model": "Civic", "year": 2024 } }'

# query vehicles
curl https://appengine.appmint.io/repository/find/vehicle \
  -H "orgid: my-org" \
  -H "Authorization: Bearer $JWT"
GET/repository/collections/{name?}/{subName?}JWT
GET/repository/find/{datatype}/{dataId?}JWT
POST/repository/find/{datatype}JWT

Validation hooks

RepositoryService runs the JSON Schema validator on every write. Failures throw a 400 with the validation errors. Beyond schema validation, collections can declare:

  • Indexes — created in MongoDB on first write, enforced thereafter (unique, sparse, compound).
  • Required role overrides — per-record requiredRole plus collection-level defaults override the global RBAC matrix.
  • Workflow — when enableWorkflow: true, writes start in draft and require approval to publish. See State and workflow.
  • History — when enableHistory: true, every change is appended to a versioned history collection. Restore via POST /repository/trash-restore.

Querying

AppEngine wraps Mongo's query language under /repository/find/{datatype} and /repository/search/{datatype}. Pass a Mongo-style filter:

curl -X POST https://appengine.appmint.io/repository/find/contact \
  -H "orgid: my-org" \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{ "filter": { "data.tags": "vip" }, "page": 1, "pageSize": 50 }'

The Dynamic Query module exposes the same engine to no-code rule builders — see the Automation pages for how rules and audiences use this.

For complex filtering across multiple collections, the platform's audience engine (CRM) and the rule engine both build on the same query layer. You don't have to roll aggregations by hand.

Listing collections

To see what's installed in your tenant:

GET/repository/collectionsJWT

The response includes both built-ins and any custom collections you've registered. The Discovery API (/discover) exposes the same data in a format meant for AI agents and integrators.