Skip to content

Collections

Collections

Each domain collection is one directory at src/collections/<lower_snake_case_id>/. The directory name is the collection ID; the model does not repeat it.

Model

import { defineModel, enums, text } from '@norbital-ai/pod/authoring';

export default defineModel(
  {
    name: text().notNull(),
    status: enums({ values: ['active', 'complete'] })
  },
  { description: 'Project site', recordLabel: 'name', icon: 'lucide:map-pin' }
);

Models carry zero presentation metadata — no .display(), importance, enumOptions, or defaultSort. Use enums([...]) for closed value sets and recordLabel for record identity.

Save the declaration as src/collections/sites/+model.ts. System collections are merged at build time and must not be redefined in tenant source.

Relationships

The single src/collections/+relationship.ts role defines relationships for the full registry and uses its adjacent generated type.

import type { Relationships } from './$types.js';

export default ((r) => ({
  sites: { site_visits: r.many.site_visits() },
  site_visits: {
    site: r.one.sites({ from: r.site_visits.site_id, to: r.sites.norbital_id })
  }
})) satisfies Relationships;

Querying via the sync engine

In tenant apps, collection data is read through the Pod sync engine: client.db.<collection>.findMany, findFirst, and count execute as live queries against a local replica. Hooks and remotes on the server still use api.db — the sync engine is the browser read and write path for operational UIs.

Companion roles

  • +hooks.ts — per-record validation and same-transaction side effects
  • +pipelines.ts — canonical collection import/export behavior
  • +integrations.ts — external receive/send bindings that reuse pipelines

Each role default-exports one declaration and uses the adjacent generated ./$types.js. No registration file is required.