Overview
Collections are structured data sets defined by a schema. Think of them like database tables but stored as JSON files (or MongoDB documents in Pro mode). Each collection has a name, a URL slug, and a list of typed fields that define the shape of every entry.
Collections power the content layer of Domma CMS. Once defined, each collection automatically gets an admin UI for managing entries and a REST API for reading and writing data.
Common use cases
Schema Definition
Each collection is defined in the admin panel with a name, slug, storage adapter, and a list of fields. The schema is stored as a JSON file in data/schemas/{slug}.schema.json and can be version-controlled alongside your project.
Below is the full schema for a typical Blog Posts collection. Each field specifies its name, type, and optional constraints such as required or default.
{
"name": "Blog Posts",
"slug": "posts",
"adapter": "file",
"fields": [
{ "name": "title", "type": "string", "required": true },
{ "name": "content", "type": "richtext" },
{ "name": "published", "type": "boolean", "default": false },
{ "name": "publishedAt", "type": "date" },
{ "name": "tags", "type": "tags" },
{ "name": "image", "type": "media" }
]
}
16 Field Types
Domma CMS supports 16 built-in field types covering text, media, relational, and specialised inputs. Every field type renders an appropriate input in the admin entry editor and applies the correct validation and serialisation.
| Type | Description | Storage |
|---|---|---|
string |
Single-line text input | String |
text |
Multi-line textarea | String |
richtext |
Rich text editor (WYSIWYG) — outputs HTML | HTML string |
number |
Numeric value — integer or decimal | Number |
boolean |
True/false toggle switch | Boolean |
date |
Date picker (no time component) | ISO 8601 date string |
datetime |
Date and time picker | ISO 8601 datetime string |
select |
Single-choice dropdown from a defined options list | String |
multiselect |
Multiple-choice dropdown from a defined options list | Array of strings |
tags |
Free-form tag input — type and press Enter to add | Array of strings |
media |
File or image picker from the Media Library | Media ID / URL string |
relation |
Link to an entry in another collection | Entry ID string |
email |
Email address with format validation | String |
url |
URL with format validation | String |
color |
Colour picker — outputs hex value | Hex string (e.g. #3b82f6) |
json |
Raw JSON input with syntax validation | Parsed JSON object/array |
Creating Collections
New collections are created through the admin panel. Once saved, the collection is immediately available via the REST API and appears in the admin navigation under Collections.
Steps to create a collection
- Navigate to Admin panel → Collections → New Collection
- Enter a human-readable Name (e.g. Blog Posts)
- The Slug is auto-generated from the name (e.g.
posts) — edit if needed - Choose a Storage adapter:
file(default) ormongo(Pro) - Click Add Field to define each field — set its name, type, and constraints
- Reorder fields by dragging the row handle
- Click Save Collection — the schema is written to
data/schemas/{slug}.schema.json
Import & export schemas
Collections can be exported as JSON schema files and re-imported into any Domma CMS instance. This is useful for sharing schemas between projects or seeding a fresh installation.
# Export a schema from the CLI
npx domma cms schema:export posts
# Import a schema file
npx domma cms schema:import ./posts.schema.json
GET /api/collections/posts/entries to read from the API.
Entry CRUD
Every collection gets a full Create, Read, Update, Delete interface in the admin panel and a matching REST API. Both surfaces are generated automatically from the field schema — no additional configuration is needed.
Admin panel operations
- Create: Admin → Collections → [Name] → New Entry → fill fields → Save
- Read: Entries are listed in a paginated, searchable admin table
- Update: Click any entry row to open the edit form → make changes → Save
- Delete: Select one or more entries → Delete (confirmation dialog shown)
- Bulk delete: Tick the header checkbox to select all visible entries → Delete
REST API endpoints
GET /api/collections/{slug}/entries
POST /api/collections/{slug}/entries
GET /api/collections/{slug}/entries/{id}
PUT /api/collections/{slug}/entries/{id}
DELETE /api/collections/{slug}/entries/{id}
All endpoints require a valid JWT bearer token unless the collection is marked public: true.
Example: create an entry via the API
const response = await H.post('/api/collections/posts/entries', {
title: 'Hello World',
content: '<p>My first post.</p>',
published: true,
publishedAt: '2026-03-13',
tags: ['intro', 'news']
});
E.toast('Post created', { type: 'success' });
FileAdapter vs MongoAdapter
Domma CMS supports two storage adapters. The default FileAdapter requires zero configuration and is ideal for small-to-medium collections. The MongoAdapter (Pro) unlocks unlimited scale and advanced query capabilities.
| Feature | FileAdapter Default | MongoAdapter Pro |
|---|---|---|
| Setup required | None | MongoDB connection string in .env |
| Storage format | JSON file — data/{slug}.json |
MongoDB collection |
| Recommended entry limit | Up to ~1,000 entries | Unlimited |
| Complex queries | Basic filter/sort only | Full MongoDB query language |
| Transactions | Not supported | Supported |
| Views & aggregations | Not supported | Supported |
| Row-level access control | Not supported | Supported |
| Version-controllable | Yes — commit data/ to git |
Via mongodump snapshots |
Switching adapters
To switch an existing collection to MongoDB, update the adapter field in the schema and run the migration command. Pro mode must be active.
// In data/schemas/posts.schema.json — change:
{ "adapter": "file" }
// To:
{ "adapter": "mongo" }
# Migrate existing JSON data into MongoDB
npx domma cms migrate:adapter posts --from=file --to=mongo
adapter: "mongo" without a valid Pro licence will fall back to the FileAdapter with a warning in the console.
8 Preset Collections
Domma CMS ships with 8 optional preset collections. These cover the most common content modelling patterns and can be seeded into a fresh installation with a single command — complete with schemas and sample data.
npm run seed
Blog posts with title, content, author, tags, and published status.
string, richtext, relation, tags, boolean, dateE-commerce items with name, price, description, image, and stock level.
string, text, number, media, booleanTeam members with name, role, bio, photo, and social links.
string, text, media, urlEvents with title, date, location, and description.
string, datetime, textQuestion and answer pairs with category grouping.
string, text, selectCustomer reviews with name, company, star rating, and review text.
string, number, text, mediaMedia gallery items with title, image, and description.
string, media, textExtended user data that links to authenticated users via a relation field.
relation, string, media, text, urlImport & Export
Collection entries can be exported to CSV or JSON and re-imported at any time. This is useful for data migrations, backups, bulk edits in a spreadsheet, or seeding a staging environment from production data.
Export entries
- Admin → Collections → [Name]
- Click Export in the toolbar
- Choose CSV or JSON
- The file downloads immediately
Import entries
- Admin → Collections → [Name]
- Click Import in the toolbar
- Upload a CSV or JSON file
- Review the preview, then confirm
Format requirements
CSV
title,published,tags
"Hello World",true,"intro,news"
"Second Post",false,""
Column headers must match field name values exactly. Array fields are comma-separated within a quoted cell.
JSON
[
{
"title": "Hello World",
"published": true,
"tags": ["intro", "news"]
},
{
"title": "Second Post",
"published": false,
"tags": []
}
]
An array of entry objects. Unknown fields are ignored; missing required fields will fail validation.
Collection Shortcode
Collection entries can be embedded directly in Markdown pages using the [collection] shortcode. The shortcode fetches entries at build time (or at request time in server-rendered mode) and renders them using the collection's configured block template.
[collection slug="posts" limit="5" orderBy="publishedAt" order="desc"]
[collection slug="team" template="card"]
[collection slug="products" filter="published:true" limit="12"]
Shortcode attributes
| Attribute | Default | Description |
|---|---|---|
slug |
— (required) | The collection slug to fetch entries from |
limit |
10 |
Maximum number of entries to render |
orderBy |
createdAt |
Field name to sort by |
order |
desc |
asc or desc |
filter |
— | Simple equality filter: field:value — multiple filters separated by spaces |
template |
list |
Block template name: list, card, grid, or a custom template path |
templates/blocks/{name}.html and have access to the full entry object as template variables.
Public API Access
By default all collection API endpoints require a valid JWT bearer token. Collections marked public: true in their schema are also accessible via a separate unauthenticated public API — useful for reading content in front-end applications without exposing credentials.
Public API (no auth)
Available when the collection schema includes "public": true. Read-only.
GET /api/public/collections/{slug}/entries
GET /api/public/collections/{slug}/entries/{id}
Supports ?limit, ?offset, ?orderBy, and ?order query parameters.
Authenticated API (JWT required)
Full CRUD access. Pass the token in the Authorization header.
GET /api/collections/{slug}/entries
POST /api/collections/{slug}/entries
PUT /api/collections/{slug}/entries/{id}
DELETE /api/collections/{slug}/entries/{id}
Example: fetch public posts with Domma HTTP
// Read public collection — no auth needed
const { entries } = await H.get('/api/public/collections/posts/entries', {
params: { limit: 5, orderBy: 'publishedAt', order: 'desc' }
});
T.create('#posts-table', {
data: entries,
columns: [
{ key: 'title', label: 'Title' },
{ key: 'publishedAt', label: 'Published' },
{ key: 'tags', label: 'Tags' }
]
});
Enabling public access on a collection
// In data/schemas/posts.schema.json — add:
{
"name": "Blog Posts",
"slug": "posts",
"adapter": "file",
"public": true,
"fields": [ ... ]
}