← Files WixARCHIVED FILE
skills/wix-app/references/data-collection/SCHEMA.md
8.07 KB · Oct 8, 2026 · 12:02 UTC
# Data Collection — declaring the schema
> The `fields` and `indexes` you write inside a collection file.
> [DATA_COLLECTION.md](../DATA_COLLECTION.md) owns scaffolding, the file shape and the CLI
> constraints.
## Field Types
| Type | Description | Use Case |
| ----------------- | -------------------------------- | ---------------------- |
| `TEXT` | Single-line text | Names, titles |
| `RICH_TEXT` | Formatted HTML text | Blog content |
| `RICH_CONTENT` | Rich content with embedded media | Complex blog posts |
| `NUMBER` | Decimal numbers | Prices, quantities |
| `BOOLEAN` | True/false | Toggles, flags |
| `DATE` | Date only | Birthdays |
| `DATETIME` | Date with time | Timestamps |
| `TIME` | Time only | Schedules |
| `IMAGE` | Single image | Thumbnails |
| `DOCUMENT` | File attachment | PDFs |
| `VIDEO` | Video file | Media |
| `AUDIO` | Audio file | Podcasts |
| `MEDIA_GALLERY` | Multiple media | Galleries |
| `REFERENCE` | Link to one item | Author → User |
| `MULTI_REFERENCE` | Link to many items | Post → Tags |
| `ADDRESS` | Structured address | Locations |
| `URL` | URL validation | Links |
| `PAGE_LINK` | Link to Wix page | Internal navigation |
| `LANGUAGE` | Language code | Multi-language content |
| `OBJECT` | JSON object | Flexible data |
| `ARRAY` | Array of values | Generic arrays |
| `ARRAY_STRING` | Array of strings | Tags list |
| `ARRAY_DOCUMENT` | Array of documents | File collections |
| `ANY` | Any type | Most flexible |
**CRITICAL: OBJECT fields require `objectOptions` with a `fields` array.** When using `type: "OBJECT"`, you MUST include `objectOptions: { fields: [] }` — the API will reject OBJECT fields without it. Use an empty `fields` array if you don't need a fixed schema (the object will still accept arbitrary JSON):
```json
{
"key": "settings",
"displayName": "Settings",
"type": "OBJECT",
"objectOptions": { "fields": [] }
}
```
> ⚠️ `objectOptions: {}` (without the `fields` key) is **not valid** and will cause a runtime error. Always include `fields`, even as an empty array.
For structured objects with a defined schema, list the nested fields inside `objectOptions.fields`:
```json
{
"key": "triggerRules",
"displayName": "Trigger Rules",
"type": "OBJECT",
"objectOptions": {
"fields": [
{ "key": "url", "displayName": "URL Condition", "type": "TEXT" },
{
"key": "scrollDepth",
"displayName": "Scroll Depth %",
"type": "NUMBER"
},
{ "key": "dateStart", "displayName": "Start Date", "type": "DATE" }
]
}
}
```
## Field Properties
```ts
{
key: 'email', // required, lowerCamelCase ASCII
type: 'TEXT', // required, see Field Types above
displayName: 'Email Address', // optional, CMS label
description: "User's primary email", // optional, help text
encrypted: false, // optional, encrypt value at rest
// arrayOptions / objectOptions / referenceOptions / multiReferenceOptions
// only when type is ARRAY / OBJECT / REFERENCE / MULTI_REFERENCE
}
```
| Property | Required | Description |
| ------------- | -------- | ------------------------------------ |
| `key` | yes | Field identifier (lowerCamelCase) |
| `type` | yes | Field data type (see Field Types) |
| `displayName` | no | Label shown in CMS |
| `description` | no | Help text |
| `encrypted` | no | Encrypt value at rest |
**There is no field-level `required`, `defaultValue`, or `unique`.** The `DevCenterDataCollectionField` type does not accept them and TypeScript will reject the build. Use these alternatives instead:
- **Required values:** Validate in the dashboard form and/or service-plugin handler before inserting. Do not rely on the collection schema to enforce presence.
- **Defaults:** Set defaults in the insert path (dashboard handler, service plugin) or via the collection's `initialData` for seeded rows.
- **Uniqueness:** Declare a unique index in the collection's `indexes` array (see [Indexes](#indexes)). Uniqueness is an index-level concern, not a field-level one.
## Indexes
**Most collections need no index at all — ship `indexes: []` unless you can name the query that
needs one.** `_id` and `_createdDate` are indexed automatically, and the budget is small: **3 regular
indexes, 1 unique, 4 total** (the collection reports its own `capabilities.indexLimits`). "Index the
field the table sorts by" is not a reason — it spends a slot for a page that would page fine without
it.
When you do add one, it is `{ fields: [{ path, order? }], unique? }` — and **nothing else**. In
particular there is **no `name`**: the runtime Create Index API takes one, but the extension builder
does not and the platform derives it (a `date` DESC index arrives as `date_DESC`). Read
`DevCenterDataCollectionIndex` in the builders package if in doubt; a stray key is a compile error on
the `satisfies DataCollection` literal.
The `indexes` array on the collection accepts entries shaped like:
```ts
indexes: [
{
fields: [{ path: 'email', order: 'ASC' }], // order is optional: 'ASC' | 'DESC'
unique: true, // optional, enforces uniqueness across items
},
{
fields: [
{ path: 'category' },
{ path: '_createdDate', order: 'DESC' },
],
},
],
```
| Property | Required | Description |
| --------------- | -------- | ------------------------------------------------------ |
| `fields` | yes | One or more `{ path, order? }` entries (composite index when more than one) |
| `fields[].path` | yes | Field key to index |
| `fields[].order`| no | `'ASC'` (default) or `'DESC'` |
| `unique` | no | Enforce uniqueness on the indexed field(s) |
Leave `indexes: []` when no custom indexing is needed; the `_id` index is created automatically.
## Naming Conventions
- **Field keys:** `lowerCamelCase`, ASCII only (e.g., `productName`, `isActive`, `createdAt`)
- **Collection IDs (`idSuffix`):** `lower-kebab-case` or `lower_underscore` (e.g., `product-categories`, `blog_posts`)
- **Display names:** Human-readable, can contain spaces (e.g., `"Product Name"`, `"Is Active"`)
## System Fields (Automatic)
Every collection includes: `_id`, `_createdDate`, `_updatedDate`, `_owner`
## Relationships
**One-to-One / Many-to-One (REFERENCE):**
```json
{
"key": "category",
"displayName": "Category",
"type": "REFERENCE",
"referenceOptions": {
"referencedCollectionId": "categories"
}
}
```
**Many-to-Many (MULTI_REFERENCE):**
```json
{
"key": "tags",
"displayName": "Tags",
"type": "MULTI_REFERENCE",
"multiReferenceOptions": {
"referencedCollectionId": "tags"
}
}
```
**CRITICAL Constraints:**
- REFERENCE/MULTI_REFERENCE fields can ONLY link to other custom CMS collections defined in your app
- The `referencedCollectionId` MUST be the `idSuffix` of another collection in the same plan
- **NEVER use REFERENCE fields to link to Wix business entities** (Products, Orders, Contacts, Members, etc.)
- Use Wix SDK APIs to access Wix business entities instead
SHA-256: 26ed7b7f81bb47691724c8ec541a20d36b30ae29c383c0c8ac0a781c1ef2c04d