---
version: 1
lastUpdated: 2026-08-02T19:14:13.087Z
sourceLang: en
contentHash: 98a39c55026d8f08
codeVerified: 2026-08-02T19:14:13.087Z
codeVerifiedHash: 98a39c55026d8f08
codeVerifiedClaims: 4
---

# Collections Builder

The Collections Builder is the no-code surface for defining LumiBase content models. Its Directus-parity contract is explicit: collection metadata, primary key strategy, storage mode, fields, relations, schema diff/apply, SDK typegen, and runtime limitations are all first-class schema data.

## 1. Collection Lifecycle

Authors create collections through the Studio wizard or by applying raw JSON. The create payload stores collection-level metadata as top-level fields, not hidden `meta` values:

- `name`, `label`, `pluralLabel`, `note`
- `hidden`, `system`, `singleton`
- `icon`, `color`
- `primaryKeyField`, `primaryKeyType`
- `storageMode`
- `displayTemplate`
- `sortField`, `archiveField`, `archiveValue`, `unarchiveValue`
- `itemDuplicationFields`, `translations`
- `accountability`, `versioning`
- `meta`

Collection names are site-scoped and must use snake_case, start with a lowercase letter, and fit the 1-63 character machine-name limit.

## 2. Primary Keys

Each collection has a logical primary key field and type.

| `primaryKeyType` | Current behavior |
|---|---|
| `nanoid` | Default string identifier generated by LumiBase. |
| `uuid` | Service-generated UUID string. |
| `string` | Caller-provided string identifier. |
| `integer` | Reserved for materialized/physical sequence-backed storage; blocked for JSONB collections. |
| `bigInteger` | Reserved for materialized/physical sequence-backed storage; blocked for JSONB collections. |

Studio blocks unsupported primary key and storage mode combinations before submit. The backend also validates them so raw JSON and SDK callers receive a precise validation error.

## 3. System Fields

Compiled schemas expose immutable system fields separately from user-defined fields. Studio renders them in a locked group and allows only safe presentation overrides such as `display`, `hidden`, `readonly`, `width`, and `translations`.

| Field | Type | Purpose |
|---|---|---|
| `id` | `string` | Primary item identifier. |
| `status` | `string` | Workflow status for draft/published/archive flows. |
| `sort` | `integer` | Manual ordering value. |
| `user_created` | `string` | User that created the item. |
| `user_updated` | `string` | User that last updated the item. |
| `created_at` | `datetime` | Creation timestamp. |
| `updated_at` | `datetime` | Last update timestamp. |
| `deleted_at` | `datetime` | Soft-delete timestamp. |

Raw schema output keeps user fields in `fields` and compiled system fields in `systemFields` so tooling can distinguish authored schema from generated runtime fields.

## 4. Fields And Layout

The Fields tab supports Directus-like metadata for each field:

- Basics: `name`, `label`, `note`, `type`, `interface`, `display`.
- Behavior: `required`, `nullable`, `readonly`, `hidden`, `encrypted`, `versioned`, `rawEnabled`.
- Storage hints: `unique`, `indexed`, `searchable`, `length`, `precision`, `scale`, `special`.
- UI and validation: `options`, `displayOptions`, `validation`, `conditions`, `translations`, `width`, `group`, `sortOrder`.

The Studio inspector preserves unknown JSON configuration so forward-compatible options are not lost when older Studio builds edit a field.

## 5. Relations

Relations are first-class schema resources. Each relation stores:

- `manyCollection`, `manyField`
- `oneCollection`, `oneField`
- `junctionCollection`
- `type`: `m2o`, `o2m`, `m2m`, or reserved `m2a`
- `aliasField`, `relatedDisplayTemplate`, `junctionManyField`, `junctionOneField`
- `sortField`, `onDelete`, `meta`

The backend validates referenced collections and fields, blocks unsafe collection deletion while relations still depend on either side, and includes relation changes in schema diff/apply results.

## 6. Raw JSON, Diff, And Apply

Raw JSON editing uses the same backend contract as the SDK:

```json
{
  "name": "posts",
  "label": "Posts",
  "primaryKeyField": "id",
  "primaryKeyType": "nanoid",
  "storageMode": "jsonb",
  "displayTemplate": "{{title}} — {{status}}",
  "fields": [
    {
      "name": "title",
      "type": "string",
      "interface": "input",
      "required": true,
      "nullable": false,
      "width": "full"
    }
  ],
  "relations": []
}
```

Use `POST /api/v1/collections/diff` to preview a schema change. Use `PUT /api/v1/collections/{name}/schema` to apply it. Apply computes the same diff, validates fields and relations, runs transactionally when the database runtime supports transactions, invalidates schema/permission/typegen caches, and emits a `schema.changed` event.

Diff entries include:

- `risk`: `low`, `medium`, or `high`
- `runtimeImpact`: `cache_invalidation`, `permission_recompile`, `typegen_rebuild`, `data_migration_required`, `relation_reindex`, or `storage_runtime_change`
- changed collection metadata, fields, and relations

Studio's Raw JSON tab disables apply until preview has run, then shows risk, runtime impact, and the raw diff.

## 7. Storage Modes

Every collection has a `storageMode`. Studio shows the mode as a badge in the collection wizard and keeps the tradeoff visible before authors create or migrate a model.

| Mode | Studio badge | Current behavior | Limitations |
|---|---|---|---|
| `jsonb` | Current | Default logical collection. Items live in the shared `items.data` JSONB document, so schema changes do not run DDL. | SQL-native unique/index constraints are advisory unless a materialized or physical projection exists. Integer primary keys are blocked in this mode. |
| `materialized` | Optimized | JSONB remains the source of truth while a managed physical projection can serve hot read paths. | Projection freshness, refresh strategy, and indexes must be managed; writes still go through the logical collection. |
| `physical` | Future | Reserved for Directus-like managed physical tables. Schema diff marks this as a storage runtime change. | Not implemented as a general DDL migration engine yet. Requires tenant-safe table naming, rollback, relation/index DDL, and online migration planning. |
| `external` | Future | Reserved for introspected external tables. | Not implemented for writes. Destructive relation actions and DDL must remain limited because LumiBase does not own the table. |

Do not present `jsonb` as equivalent to Directus physical tables. The current product promise is fast schema evolution first, with materialized projections for performance and a future physical/external decision tracked in `docs/en/architecture/physical-collections.md`.

## 8. SDK And Typegen

The SDK exposes schema resources under `client.schema`:

```ts
client.schema.collections.list();
client.schema.fields.rename("posts", "headline", "title", {
  type: "string",
  interface: "input",
  confirmRiskyChange: true,
});
client.schema.relations.create({
  manyCollection: "posts",
  manyField: "author_id",
  oneCollection: "authors",
  type: "m2o",
});
client.schema.diff("posts", proposedSchema);
client.schema.apply("posts", proposedSchema);
```

Legacy flat methods such as `schema.listCollections()` and `schema.upsertField()` remain available. SDK errors preserve backend `code`, `path`, and `risk` metadata through `LumiError.body`.

Typegen manifest version `2` includes primary key metadata, system fields, nullable/required, readonly/generated flags, encrypted field read behavior, and relation descriptors. Generated output includes base collection interfaces and relation-expanded response types such as `PostsExpanded`.
