Database & Migrations
How the Content Engine maps content models to PostgreSQL tables using createContentModel, handles system columns, and executes Drizzle Kit migrations.
Content Engine content types compile directly into standard PostgreSQL database tables via Drizzle ORM.
Prerequisites & Context
Before setting up database tables, you must have a content type definition (e.g. articleContentType) created using defineContentType in src/content/article.ts:
import { defineContentType, field } from '@vitnode/core/content'
// 1. Client-safe definition (imported by both API and AdminCP)
export const articleContentType = defineContentType({
id: 'example.article',
tableName: 'example_articles',
fields: {
title: field.text({ required: true }),
code: field.text({ required: true }),
},
})createContentModel(definition, options): A backend utility from@vitnode/core/content/serverthat converts a client-safe content definition into a server-side Drizzle ORM model.articleContent: The compiled model object containing.table(raw Drizzle table),.service(c)(CRUD helper), and.schemas(Zod schemas).
Step-by-Step Database Setup
Step 1: Create Database Model File
Create your model file under src/database/articles.ts. Import createContentModel from @vitnode/core/content/server and your definition articleContentType:
import { createContentModel } from '@vitnode/core/content/server'
import { articleContentType } from '@/content/article'
import { example_categories } from './categories'
// Compile client definition into a server Drizzle model
export const articleContent = createContentModel(articleContentType, {
references: {
category: () => example_categories.id,
},
})
// Export raw Drizzle table for migration discovery
export const example_articles = articleContent.tableStep 2: Add Database Indexes
Add single or composite indexes inside your content definition file src/content/article.ts:
export const articleContentType = defineContentType({
id: 'example.article',
tableName: 'example_articles',
fields: {
title: field.text({ required: true }),
code: field.text({ required: true }),
status: field.enum({ values: ['draft', 'published'] }),
},
indexes: [
{ on: ['status', 'createdAt'] }, // Composite index
{ on: ['code'], unique: true }, // Unique constraint
],
})Step 3: Run Database Migrations
Compile your plugins and apply schema changes:
bun run build:plugins && bun run db:migratepnpm build:plugins && pnpm db:migratenpm run build:plugins && npm run db:migrateAutomatic System Columns
When createContentModel compiles a model, it automatically includes standard system columns:
| Column | Postgres Type | Description |
|---|---|---|
id | serial / integer | Primary key identifier |
createdAt | timestamp | Auto-populated creation timestamp |
updatedAt | timestamp | Auto-updated modification timestamp |
If publication is enabled, status and publishedAt columns are added.
If editorial is enabled, a version column is added for optimistic concurrency control.