Content Engine
Production & Security
Step-by-step guide to concurrency control, security enforcement, failure retries, performance scaling, and system limitations.
Deploying Content Engine models to production environments requires understanding concurrency protections, database security, performance indexing, and architectural boundaries.
Prerequisites & Context
Production hardening applies to both your client definition articleContentType and compiled server model articleContent:
- Concurrency Locks:
ContentVersionConflictis thrown when updating aneditorial: truecontent type whoseversionhas changed concurrently. - Staff Permissions: Enforced automatically by
buildContentAdminModulefor all CRUD routes. - Diagnostics:
getContentEngineDiagnostics()provides startup and registered content type metrics.
Step-by-Step Production Best Practices
Step 1: Handle Concurrency & Optimistic Lock Retries
When editorial is enabled, update operations expect a matching version integer. Handle ContentVersionConflict by re-fetching and retrying:
import { ContentVersionConflict } from '@vitnode/core/content'
try {
await service.update(articleId, { title: 'New Title', version: 2 })
} catch (err) {
if (err instanceof ContentVersionConflict) {
// Refetch latest record version and retry update
const latest = await service.findById(articleId)
await service.update(articleId, {
title: 'New Title',
version: latest.version,
})
}
}Step 2: Enforce Staff Permissions and Public Allowlists
Ensure all custom administrative actions check staff permissions, and verify that publicApi.fields lists only non-sensitive attributes:
export const articleContentType = defineContentType({
id: 'example.article',
tableName: 'example_articles',
publication: true,
publicApi: {
fields: ['title', 'code', 'excerpt'],
},
fields: {
title: field.text({ required: true }),
code: field.text({ required: true }),
excerpt: field.textarea({ nullable: true }),
internalNotes: field.textarea({ nullable: true }), // Private internal column
},
})Step 3: Inspect Engine Diagnostics
Log diagnostic info during startup or debugging:
import { getContentEngineDiagnostics } from '@vitnode/core/content/server'
const diagnostics = getContentEngineDiagnostics()
console.log(`Registered Content Types: ${diagnostics.totalContentTypes}`)Architectural Boundaries & System Limitations
- Code-First Architecture: Content types must be declared in TypeScript source control and compiled before migrations are generated. There is no runtime no-code UI for creating tables dynamically.
- No Direct Schema Polymorphism: A single field cannot reference multiple unrelated tables. Use structured JSON fields or separate relation tables instead.