Collections

Define content types with TypeScript schemas and field validation. Collections are the foundation of your content model in SonicJS.

Overview

Collections define your content types using TypeScript schemas. Each collection automatically gets:

  • Type-safe database schema
  • Automatic API endpoints
  • Admin UI for content management
  • Field validation and constraints
  • Version control through git

Two Types of Collections

Config-Managed Collections:

  • Defined in TypeScript files (src/collections/*.collection.ts)
  • Version controlled with your codebase
  • Automatically synced on app startup
  • Locked from editing in the admin UI (marked with "Config" badge)
  • Type-safe with IDE autocomplete

UI-Created Collections:

  • Created and edited through the admin interface
  • Stored directly in the database
  • Not version controlled
  • Fully editable in the UI
  • Great for rapid prototyping

Creating Collections

Basic Collection

Create a new file in src/collections/ with a .collection.ts extension:

Blog Posts Collection

// src/collections/blog-posts.collection.ts
import { CollectionConfig } from '@sonicjs-cms/core'

export const blogPostsCollection: CollectionConfig = {
  name: 'blog_posts',
  displayName: 'Blog Posts',
  description: 'Articles and blog content',
  schema: {
    type: 'object',
    properties: {
      title: {
        type: 'string',
        required: true,
        minLength: 3,
        maxLength: 200
      },
      slug: {
        type: 'slug',
        required: true
      },
      content: {
        type: 'richtext',
        required: true
      },
      excerpt: {
        type: 'textarea',
        maxLength: 500
      },
      author: {
        type: 'reference',
        collection: 'users',
        required: true
      },
      publishDate: {
        type: 'datetime',
        required: true
      },
      status: {
        type: 'select',
        enum: ['draft', 'published', 'archived'],
        default: 'draft'
      }
    }
  },
  icon: '📝',
  color: 'blue',
  managed: true,
  isActive: true,
  defaultSort: 'created_at',
  defaultSortOrder: 'desc',
  listFields: ['title', 'author', 'publishDate', 'status'],
  searchFields: ['title', 'excerpt', 'content']
}

Syncing Collections

Collections are automatically synced on server startup. You can also manually sync:

Sync Collections

# Collections auto-sync on server restart
npm run dev

# Or manually sync
npm run sync-collections

Field Types

SonicJS supports 30+ field types for building rich content schemas:

Text Fields

  • Name
    string
    Type
    string
    Description

    Single-line text input. Use for titles, names, short text.

  • Name
    textarea
    Type
    string
    Description

    Multi-line plain text. Use for descriptions, notes, long text.

  • Name
    email
    Type
    string
    Description

    Email with validation. Use for contact emails, author info.

  • Name
    url
    Type
    string
    Description

    URL with validation. Use for links, external resources.

  • Name
    slug
    Type
    string
    Description

    URL-friendly identifier. Use for page URLs, SEO paths.

  • Name
    color
    Type
    string
    Description

    Color picker. Use for theme colors, UI customization.

Rich Content

  • Name
    richtext
    Type
    string
    Description

    WYSIWYG HTML editor. Use for blog posts, articles, formatted content.

  • Name
    markdown
    Type
    string
    Description

    Markdown editor. Use for documentation, technical content.

  • Name
    json
    Type
    object
    Description

    JSON editor. Use for structured data, API responses.

Numbers and Dates

  • Name
    number
    Type
    number
    Description

    Numeric input. Use for prices, quantities, ratings.

  • Name
    date
    Type
    string
    Description

    Date picker (no time). Use for birthdays, deadlines.

  • Name
    datetime
    Type
    string
    Description

    Date and time picker. Use for publish dates, events.

Selections

  • Name
    select
    Type
    string
    Description

    Dropdown (single choice). Use for categories, status fields.

  • Name
    multiselect
    Type
    array
    Description

    Dropdown (multiple choices). Use for tags, multiple categories.

  • Name
    radio
    Type
    string
    Description

    Radio buttons. Use for status, visibility options.

  • Name
    checkbox
    Type
    boolean
    Description

    Boolean checkbox. Use for feature toggles, flags.

Media and Files

  • Name
    media
    Type
    string
    Description

    Image/media picker. Use for featured images, avatars.

  • Name
    file
    Type
    string
    Description

    File upload. Use for PDFs, documents, downloads.

Relationships

  • Name
    reference
    Type
    string
    Description

    Reference to another collection. Use for authors, categories.

  • Name
    array
    Type
    array
    Description

    Array of items. Can contain any field type.


Validation

Built-in Validators

Field Validation

export const productsCollection: CollectionConfig = {
  name: 'products',
  displayName: 'Products',
  schema: {
    type: 'object',
    properties: {
      name: {
        type: 'string',
        required: true,
        minLength: 3,
        maxLength: 200
      },
      sku: {
        type: 'string',
        required: true,
        pattern: '^[A-Z0-9-]+$'  // Only uppercase, numbers, hyphens
      },
      price: {
        type: 'number',
        required: true,
        minimum: 0,
        maximum: 999999
      },
      email: {
        type: 'email',
        required: true
      },
      url: {
        type: 'url',
        required: false
      },
      status: {
        type: 'select',
        enum: ['draft', 'active', 'archived'],
        default: 'draft'
      }
    }
  },
  managed: true,
  isActive: true
}

Validation Rules

  • required - Field must have a value
  • minLength / maxLength - String length constraints
  • minimum / maximum - Number range constraints
  • pattern - Regular expression validation
  • enum - Must be one of specified values
  • default - Default value if not provided

Examples

E-commerce Product

Product Collection

export const productsCollection: CollectionConfig = {
  name: 'products',
  displayName: 'Products',
  description: 'E-commerce product catalog',
  schema: {
    type: 'object',
    properties: {
      name: {
        type: 'string',
        required: true
      },
      sku: {
        type: 'string',
        required: true
      },
      price: {
        type: 'number',
        required: true,
        minimum: 0
      },
      description: {
        type: 'richtext'
      },
      images: {
        type: 'array',
        items: {
          type: 'media',
          accept: 'image/*'
        }
      },
      category: {
        type: 'select',
        enum: ['electronics', 'clothing', 'home', 'sports']
      },
      inStock: {
        type: 'boolean',
        default: true
      },
      tags: {
        type: 'array',
        items: { type: 'string' }
      }
    }
  },
  icon: '🛍️',
  managed: true,
  isActive: true
}

Documentation Page

Docs Collection

export const docsCollection: CollectionConfig = {
  name: 'documentation',
  displayName: 'Documentation',
  schema: {
    type: 'object',
    properties: {
      title: {
        type: 'string',
        required: true
      },
      slug: {
        type: 'slug',
        required: true
      },
      content: {
        type: 'markdown',
        required: true
      },
      category: {
        type: 'select',
        enum: ['getting-started', 'api', 'guides', 'reference']
      },
      order: {
        type: 'number',
        default: 0
      },
      published: {
        type: 'boolean',
        default: false
      }
    }
  },
  icon: '📚',
  managed: true,
  isActive: true
}

Event Management

Events Collection

export const eventsCollection: CollectionConfig = {
  name: 'events',
  displayName: 'Events',
  schema: {
    type: 'object',
    properties: {
      title: {
        type: 'string',
        required: true
      },
      description: {
        type: 'richtext'
      },
      startDate: {
        type: 'datetime',
        required: true
      },
      endDate: {
        type: 'datetime',
        required: true
      },
      location: {
        type: 'string'
      },
      virtualLink: {
        type: 'url'
      },
      capacity: {
        type: 'number',
        minimum: 1
      },
      registration: {
        type: 'boolean',
        default: false
      },
      organizer: {
        type: 'reference',
        collection: 'users'
      }
    }
  },
  icon: '📅',
  managed: true,
  isActive: true
}

Using Collections

Query Content

Fetch Collection Content

// Get all content from a collection
const posts = await db.select()
  .from(content)
  .where(eq(content.collection_id, 'blog_posts'))
  .orderBy(desc(content.created_at))
  .limit(10)

// Get single content item
const post = await db.select()
  .from(content)
  .where(eq(content.id, postId))
  .get()

// Search content
const results = await db.select()
  .from(content)
  .where(
    and(
      eq(content.collection_id, 'blog_posts'),
      like(content.title, '%search%')
    )
  )
  .all()

API Endpoints

Collections automatically get REST API endpoints:

GET/api/collections/{collection}/content
Get all content from a collection
GET/api/content/{id}
Get single content item
POST/admin/contentAuth required
Create new content
PUT/admin/content/{id}Auth required
Update content
DELETE/admin/content/{id}Auth required
Delete content

Best Practices

Collection Design Tips

  • Use descriptive field names that reflect your content model
  • Set appropriate validation rules to maintain data quality
  • Use references for relationships between collections
  • Enable search on fields that users will query frequently
  • Set reasonable defaults for optional fields

Performance Considerations

  • Limit the number of fields in listFields to improve list performance
  • Use appropriate field types (e.g., select instead of string for fixed options)
  • Index frequently queried fields in your database

Next Steps

Was this page helpful?