Schema Design
Best practices for designing Directus data models using the collections, fields, and schema MCP tools.
Design Approach
- Explore first — Use
schema tool (discovery mode) to see existing collections
- Plan on paper — List entities, fields, and relationships before building
- Build in order — Follow the creation order below strictly
- Verify — Use
schema tool (detailed mode) to confirm structure
Creation Order (Critical)
Build schema in this exact order to avoid dependency errors:
- Collection folders — UI-only grouping (optional)
- Independent collections — No foreign key dependencies (categories, tags, statuses)
- Dependent collections — Main entities that reference other collections (posts, products)
- Junction collections — For M2M relationships (posts_tags, product_categories)
- Basic fields — Non-relational fields (string, text, integer, boolean, etc.)
- Relational fields — M2O uuid fields, O2M/M2M alias fields
- Relations — Define relationships after both collections and fields exist
- Sample data — Create test items to verify schema
Creating a Collection
Tool: collections
Input: {
"action": "create",
"data": [{
"collection": "articles",
"schema": {},
"meta": {
"icon": "article",
"note": "Blog articles collection",
"color": "#2196F3",
"sort_field": "sort",
"archive_field": "status",
"archive_value": "archived",
"unarchive_value": "draft",
"display_template": "{{title}}",
"accountability": "all"
}
}]
}
Schema Rules
"schema": {} — real database table
"schema": null — folder-only (no table, just UI grouping)
Collection Folders
Group collections in the sidebar:
{
"action": "create",
"data": [{
"collection": "content",
"schema": null,
"meta": {
"icon": "folder",
"note": "Content-related collections",
"color": "#4CAF50"
}
}]
}
Then assign collections to the folder:
Tool: collections
Input: {
"action": "update",
"data": [{
"collection": "articles",
"meta": { "group": "content" }
}]
}
System Fields
Add these recommended fields to every content collection:
| Field |
Type |
Purpose |
id |
uuid |
Primary key (auto-generated on collection create) |
status |
string |
Workflow status (draft/published/archived) |
sort |
integer |
Manual sort order |
user_created |
uuid |
Auto-tracked creator (system field) |
user_updated |
uuid |
Auto-tracked last editor (system field) |
date_created |
timestamp |
Auto-tracked creation date (system field) |
date_updated |
timestamp |
Auto-tracked update date (system field) |
Adding System Fields
Tool: fields
Input: {
"action": "create",
"collection": "articles",
"data": [
{
"field": "status",
"type": "string",
"meta": {
"interface": "select-dropdown",
"options": {
"choices": [
{ "text": "Draft", "value": "draft" },
{ "text": "Published", "value": "published" },
{ "text": "Archived", "value": "archived" }
]
},
"display": "labels",
"width": "half"
},
"schema": { "default_value": "draft", "is_nullable": false }
},
{
"field": "sort",
"type": "integer",
"meta": { "interface": "input", "hidden": true }
},
{
"field": "user_created",
"type": "uuid",
"meta": {
"special": ["user-created"],
"interface": "select-dropdown-m2o",
"display": "user",
"readonly": true,
"hidden": true,
"width": "half"
}
},
{
"field": "date_created",
"type": "timestamp",
"meta": {
"special": ["date-created"],
"interface": "datetime",
"display": "datetime",
"readonly": true,
"hidden": true,
"width": "half"
}
},
{
"field": "user_updated",
"type": "uuid",
"meta": {
"special": ["user-updated"],
"interface": "select-dropdown-m2o",
"display": "user",
"readonly": true,
"hidden": true,
"width": "half"
}
},
{
"field": "date_updated",
"type": "timestamp",
"meta": {
"special": ["date-updated"],
"interface": "datetime",
"display": "datetime",
"readonly": true,
"hidden": true,
"width": "half"
}
}
]
}
Singleton Collections
For global settings, site config, or any single-record collection:
{
"action": "create",
"data": [{
"collection": "site_settings",
"schema": {},
"meta": {
"singleton": true,
"icon": "settings",
"note": "Global site configuration"
}
}]
}
Singletons show as a single form (no list view) in the Directus app.
Content Versioning
Enable version tracking for editorial workflows:
Tool: collections
Input: {
"action": "update",
"data": [{
"collection": "articles",
"meta": { "versioning": true }
}]
}
Allows creating content versions (drafts) before publishing changes.
Display Templates
Control how items appear in relation dropdowns and lists:
"meta": {
"display_template": "{{title}} — {{author.first_name}} {{author.last_name}}"
}
Supports field references with {{field_name}} and relation traversal with dot notation.
Archive Pattern
Soft-delete pattern using archive fields:
"meta": {
"archive_field": "status",
"archive_value": "archived",
"unarchive_value": "draft",
"archive_app_filter": true
}
When archive_app_filter: true, archived items are hidden by default in the app.
Common Design Patterns
Blog CMS
| Collection |
Key Fields |
Relations |
authors |
name, bio, avatar, email |
— |
categories |
name, slug, description |
— |
tags |
name, slug |
— |
posts |
title, slug, content, excerpt, featured_image, status |
M2O → authors, M2M ↔ categories, M2M ↔ tags |
posts_categories |
(junction) |
M2O → posts, M2O → categories |
posts_tags |
(junction) |
M2O → posts, M2O → tags |
E-Commerce
| Collection |
Key Fields |
Relations |
brands |
name, logo, description |
— |
categories |
name, slug, parent |
Self-referencing M2O |
products |
name, sku, price, description, status |
M2O → brands, M2M ↔ categories |
variants |
sku, price, stock, attributes |
M2O → products |
orders |
number, total, status, customer_email |
— |
order_items |
quantity, price |
M2O → orders, M2O → variants |
Project Management
| Collection |
Key Fields |
Relations |
projects |
name, description, status, deadline |
— |
tasks |
title, description, priority, status, due_date |
M2O → projects, M2O → directus_users (assignee) |
comments |
text, date |
M2O → tasks, M2O → directus_users |
labels |
name, color |
— |
tasks_labels |
(junction) |
M2O → tasks, M2O → labels |
Primary Key Guidance
- UUID (recommended) — Generated automatically, globally unique, best for distributed systems
- Auto-increment integer — Simpler, readable IDs, but less portable
Directus creates a UUID id field by default when you create a collection.
Common Mistakes
- Creating relations before collections exist — Both collections must exist first
- Wrong creation order — Independent collections first, then dependent, then junction
- Everything in one collection — Normalize data; use relations instead of JSON fields for structured data
- Missing system fields — Always add status, user_created, date_created for content collections
- No display templates — Set
display_template so items are recognizable in relation dropdowns
- Forgetting archive pattern — Use archive fields for soft-delete instead of actual deletion
- Not using collection folders — Organize collections in folders for large projects
1---2name: schema-design-23description: Schema design best practices — data modeling, collection planning, system fields, display templates, singletons, folders, versioning. This skill should be used when the user asks to design a data model, plan collections, create a database schema, or build a CMS/e-commerce/project database structure in Directus.4---56# Schema Design78Best practices for designing Directus data models using the `collections`, `fields`, and `schema` MCP tools.910## Design Approach11121. **Explore first** — Use `schema` tool (discovery mode) to see existing collections132. **Plan on paper** — List entities, fields, and relationships before building143. **Build in order** — Follow the creation order below strictly154. **Verify** — Use `schema` tool (detailed mode) to confirm structure1617## Creation Order (Critical)1819Build schema in this exact order to avoid dependency errors:20211. **Collection folders** — UI-only grouping (optional)222. **Independent collections** — No foreign key dependencies (categories, tags, statuses)233. **Dependent collections** — Main entities that reference other collections (posts, products)244. **Junction collections** — For M2M relationships (posts_tags, product_categories)255. **Basic fields** — Non-relational fields (string, text, integer, boolean, etc.)266. **Relational fields** — M2O uuid fields, O2M/M2M alias fields277. **Relations** — Define relationships after both collections and fields exist288. **Sample data** — Create test items to verify schema2930## Creating a Collection3132```json33Tool: collections34Input: {35 "action": "create",36 "data": [{37 "collection": "articles",38 "schema": {},39 "meta": {40 "icon": "article",41 "note": "Blog articles collection",42 "color": "#2196F3",43 "sort_field": "sort",44 "archive_field": "status",45 "archive_value": "archived",46 "unarchive_value": "draft",47 "display_template": "{{title}}",48 "accountability": "all"49 }50 }]51}52```5354### Schema Rules5556- `"schema": {}` — real database table57- `"schema": null` — folder-only (no table, just UI grouping)5859### Collection Folders6061Group collections in the sidebar:6263```json64{65 "action": "create",66 "data": [{67 "collection": "content",68 "schema": null,69 "meta": {70 "icon": "folder",71 "note": "Content-related collections",72 "color": "#4CAF50"73 }74 }]75}76```7778Then assign collections to the folder:7980```json81Tool: collections82Input: {83 "action": "update",84 "data": [{85 "collection": "articles",86 "meta": { "group": "content" }87 }]88}89```9091## System Fields9293Add these recommended fields to every content collection:9495| Field | Type | Purpose |96|-------|------|---------|97| `id` | `uuid` | Primary key (auto-generated on collection create) |98| `status` | `string` | Workflow status (draft/published/archived) |99| `sort` | `integer` | Manual sort order |100| `user_created` | `uuid` | Auto-tracked creator (system field) |101| `user_updated` | `uuid` | Auto-tracked last editor (system field) |102| `date_created` | `timestamp` | Auto-tracked creation date (system field) |103| `date_updated` | `timestamp` | Auto-tracked update date (system field) |104105### Adding System Fields106107```json108Tool: fields109Input: {110 "action": "create",111 "collection": "articles",112 "data": [113 {114 "field": "status",115 "type": "string",116 "meta": {117 "interface": "select-dropdown",118 "options": {119 "choices": [120 { "text": "Draft", "value": "draft" },121 { "text": "Published", "value": "published" },122 { "text": "Archived", "value": "archived" }123 ]124 },125 "display": "labels",126 "width": "half"127 },128 "schema": { "default_value": "draft", "is_nullable": false }129 },130 {131 "field": "sort",132 "type": "integer",133 "meta": { "interface": "input", "hidden": true }134 },135 {136 "field": "user_created",137 "type": "uuid",138 "meta": {139 "special": ["user-created"],140 "interface": "select-dropdown-m2o",141 "display": "user",142 "readonly": true,143 "hidden": true,144 "width": "half"145 }146 },147 {148 "field": "date_created",149 "type": "timestamp",150 "meta": {151 "special": ["date-created"],152 "interface": "datetime",153 "display": "datetime",154 "readonly": true,155 "hidden": true,156 "width": "half"157 }158 },159 {160 "field": "user_updated",161 "type": "uuid",162 "meta": {163 "special": ["user-updated"],164 "interface": "select-dropdown-m2o",165 "display": "user",166 "readonly": true,167 "hidden": true,168 "width": "half"169 }170 },171 {172 "field": "date_updated",173 "type": "timestamp",174 "meta": {175 "special": ["date-updated"],176 "interface": "datetime",177 "display": "datetime",178 "readonly": true,179 "hidden": true,180 "width": "half"181 }182 }183 ]184}185```186187## Singleton Collections188189For global settings, site config, or any single-record collection:190191```json192{193 "action": "create",194 "data": [{195 "collection": "site_settings",196 "schema": {},197 "meta": {198 "singleton": true,199 "icon": "settings",200 "note": "Global site configuration"201 }202 }]203}204```205206Singletons show as a single form (no list view) in the Directus app.207208## Content Versioning209210Enable version tracking for editorial workflows:211212```json213Tool: collections214Input: {215 "action": "update",216 "data": [{217 "collection": "articles",218 "meta": { "versioning": true }219 }]220}221```222223Allows creating content versions (drafts) before publishing changes.224225## Display Templates226227Control how items appear in relation dropdowns and lists:228229```json230"meta": {231 "display_template": "{{title}} — {{author.first_name}} {{author.last_name}}"232}233```234235Supports field references with `{{field_name}}` and relation traversal with dot notation.236237## Archive Pattern238239Soft-delete pattern using archive fields:240241```json242"meta": {243 "archive_field": "status",244 "archive_value": "archived",245 "unarchive_value": "draft",246 "archive_app_filter": true247}248```249250When `archive_app_filter: true`, archived items are hidden by default in the app.251252## Common Design Patterns253254### Blog CMS255256| Collection | Key Fields | Relations |257|-----------|------------|-----------|258| `authors` | name, bio, avatar, email | — |259| `categories` | name, slug, description | — |260| `tags` | name, slug | — |261| `posts` | title, slug, content, excerpt, featured_image, status | M2O → authors, M2M ↔ categories, M2M ↔ tags |262| `posts_categories` | (junction) | M2O → posts, M2O → categories |263| `posts_tags` | (junction) | M2O → posts, M2O → tags |264265### E-Commerce266267| Collection | Key Fields | Relations |268|-----------|------------|-----------|269| `brands` | name, logo, description | — |270| `categories` | name, slug, parent | Self-referencing M2O |271| `products` | name, sku, price, description, status | M2O → brands, M2M ↔ categories |272| `variants` | sku, price, stock, attributes | M2O → products |273| `orders` | number, total, status, customer_email | — |274| `order_items` | quantity, price | M2O → orders, M2O → variants |275276### Project Management277278| Collection | Key Fields | Relations |279|-----------|------------|-----------|280| `projects` | name, description, status, deadline | — |281| `tasks` | title, description, priority, status, due_date | M2O → projects, M2O → directus_users (assignee) |282| `comments` | text, date | M2O → tasks, M2O → directus_users |283| `labels` | name, color | — |284| `tasks_labels` | (junction) | M2O → tasks, M2O → labels |285286## Primary Key Guidance287288- **UUID** (recommended) — Generated automatically, globally unique, best for distributed systems289- **Auto-increment integer** — Simpler, readable IDs, but less portable290291Directus creates a UUID `id` field by default when you create a collection.292293## Common Mistakes2942951. **Creating relations before collections exist** — Both collections must exist first2962. **Wrong creation order** — Independent collections first, then dependent, then junction2973. **Everything in one collection** — Normalize data; use relations instead of JSON fields for structured data2984. **Missing system fields** — Always add status, user_created, date_created for content collections2995. **No display templates** — Set `display_template` so items are recognizable in relation dropdowns3006. **Forgetting archive pattern** — Use archive fields for soft-delete instead of actual deletion3017. **Not using collection folders** — Organize collections in folders for large projects