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: ContentVersionConflict is thrown when updating an editorial: true content type whose version has changed concurrently.
  • Staff Permissions: Enforced automatically by buildContentAdminModule for 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:

src/content/article.ts
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.