Laizy CMS
API Reference

Schema Sync

How schema synchronization works -- from .laizy files to database, with safety checks and migration plans.

Schema Sync

Schema synchronization is the process that keeps your local .laizy schema files in sync with the database. It compares what you have defined locally against what exists remotely, generates a migration plan with safety classifications, and executes the changes after confirmation.

The Sync Lifecycle

When you run laizy sync, the system follows a five-stage pipeline:

Parse → Diff → Plan → Enhance → Execute

1. Parse

The local schema file (default: laizy/schema.laizy) is parsed into an abstract syntax tree (AST). Each model block becomes a ModelDefinition with typed fields and constraints:

interface ModelDefinition {
  type: 'model';
  name: string;
  fields: Array<{
    name: string;
    fieldType: 'String' | 'Int' | 'Float' | 'Boolean' | 'DateTime';
    constraints: {
      required?: boolean;
      unique?: boolean;
      default?: string | number | boolean;
      maxLength?: number;
      minLength?: number;
    };
  }>;
}

If the schema has syntax errors, parsing fails with line numbers pointing to the problem:

Schema parsing failed
Error: Line 5: Expected ":" after field name

2. Diff

The compareSchemas() function compares the local AST against the current remote state (fetched from the API via getSchemaState()). It produces a SchemaDiff:

interface SchemaDiff {
  toCreate: ModelDefinition[];       // New models to add
  toUpdate: ModelUpdate[];           // Existing models to modify
  toDelete: RemoteContentModel[];    // Models to remove
}

For updated models, the differ computes field-level changes:

interface FieldChange {
  type: 'added' | 'removed' | 'modified';
  field: FieldDefinition;
  impact: 'safe' | 'warning' | 'destructive';
  migrationStrategy?: {
    type: 'auto_fill' | 'manual' | 'none';
    defaultValue?: any;
    description: string;
  };
}

3. Plan

The generateMigrationPlan() function converts the diff into an ordered list of operations:

interface MigrationPlan {
  operations: MigrationOperation[];
  safetyReport: SafetyReport;
  requiresConfirmation: boolean;
}

interface MigrationOperation {
  type: 'create_model' | 'update_model' | 'delete_model';
  modelName: string;
  impact: 'safe' | 'warning' | 'destructive';
  description: string;
  contentCount?: number;
}

4. Enhance

The SchemaSyncOrchestrator enhances the migration plan with content impact data. For each affected model, it queries the API for the number of existing content entries and annotates the operations:

// Before enhancement:
// "Create new content model "BlogPost" with 4 fields"

// After enhancement:
// "Create new content model "BlogPost" with 4 fields (no existing content)"
// "Delete content model "OldPage" and all associated content (3 content entries affected)"

5. Execute

If the plan is not a dry run, the orchestrator sends the migration plan to the server for execution. The server processes each operation in order and returns a result:

interface MigrationResult {
  success: boolean;
  operationsCompleted: number;
  totalOperations: number;
  errors?: string[];
}

Impact Classification

Every operation in the migration plan is classified into one of three impact levels:

Safe

No existing data is affected. Creating new models and adding optional fields are always safe.

ChangeImpact
Create a new modelSafe
Add an optional fieldSafe
Add a field with a default valueSafe
Make a required field optionalSafe
Change constraints (unique, maxLength)Safe

Warning

Existing data may need updates but will not be lost. The CLI shows a warning and asks for confirmation.

ChangeImpact
Add a required field (no default)Warning -- auto-fills with type default
Change Int to FloatWarning -- lossless conversion
Change Float to IntWarning -- may lose decimal precision
Change from String to another typeWarning -- depends on content
Make an optional field requiredWarning -- null values need defaults

Destructive

Data will be permanently lost. The CLI shows an explicit warning and requires confirmation.

ChangeImpact
Delete a modelDestructive -- all content entries removed
Remove a fieldDestructive -- field data deleted
Change Boolean to non-BooleanDestructive -- incompatible conversion
Change DateTime to non-DateTimeDestructive -- incompatible conversion

Auto-Fill Defaults

When a required field is added to a model that already has content, the system auto-fills existing entries with a type-appropriate default value:

Field TypeAuto-Fill Default
String"" (empty string)
Int0
Float0.0
Booleanfalse
DateTimenull

If the field has a default constraint in the schema, that value is used instead.

Safety Report

Every migration plan includes a safety report summarizing the risk:

interface SafetyReport {
  safeOperations: number;
  warningOperations: number;
  destructiveOperations: number;
  warnings: string[];          // Human-readable warning messages
  blockers: string[];          // Currently unused, reserved for future hard blockers
}

The plan requires confirmation if warningOperations > 0 or destructiveOperations > 0. Example output:

Migration Plan:

  Operation       Model       Impact         Description
  create_model    Product     safe           Create new content model "Product" with 3 fields (no existing content)
  update_model    BlogPost    warning        Update model "BlogPost" (2 field changes) (15 entries affected)
  delete_model    OldPage     destructive    Delete content model "OldPage" and all associated content (3 entries affected)

DESTRUCTIVE: Deleting model "OldPage" will permanently remove all content data (3 content entries will be affected)

? Are you absolutely sure you want to continue? This cannot be undone. (y/N)

Dry Run Mode

Preview the full migration plan without executing anything:

pnpm laizy sync --dry-run

Dry run performs the entire pipeline (parse, diff, plan, enhance) but stops before execution. Use this to review changes before committing.

Migration Plan:

  Operation       Model      Impact    Description
  create_model    Product    safe      Create new content model "Product" with 4 fields

Dry run complete - no changes made

Force Mode

Skip all confirmation prompts and execute immediately:

pnpm laizy sync --force

Force mode bypasses all safety confirmations including destructive changes. Only use this in CI/CD pipelines after verifying the plan with --dry-run.

Using the Orchestrator Programmatically

The SchemaSyncOrchestrator can be used directly in your code for custom sync workflows:

import { SchemaSyncOrchestrator } from '@/lib/schema/sync/schema-sync-orchestrator';
import { ManagementClient } from '@/lib/management-client';

const client = new ManagementClient({
  baseUrl: 'https://laizycms.com',
  apiToken: process.env.LAIZY_API_TOKEN!,
  projectId: 'your-project-id',
});

const orchestrator = new SchemaSyncOrchestrator(client);

// Get the diff without executing
const diff = await orchestrator.getDiff(localModels);

// Generate a plan with content impact
const plan = await orchestrator.planMigration(localModels);

// Execute the full sync
const result = await orchestrator.syncSchemas(localModels, {
  projectId: 'your-project-id',
  dryRun: false,
  force: true,
});

console.log(result.message);
// "Migration completed successfully. 3/3 operations executed."

There are also convenience functions for common operations:

import {
  syncSchemas,
  getSchemaDiff,
  planSchemaMigration,
} from '@/lib/schema/sync/schema-sync-orchestrator';

// One-liner sync
const result = await syncSchemas(client, localModels, {
  projectId: 'your-project-id',
  dryRun: false,
  force: true,
});

// One-liner diff
const diff = await getSchemaDiff(client, localModels);

// One-liner plan
const plan = await planSchemaMigration(client, localModels);

Error Handling

Parse errors

Schema syntax errors stop the pipeline immediately:

Schema parsing failed
Error: Line 12: Unexpected token "{"

Authentication errors

If the API token is invalid or expired:

Analysis failed
Error: unauthorized

This might be an authentication issue.
Try running laizy init to update your credentials.

Execution errors

If an operation fails during migration, the result includes error details. Operations that completed before the failure are not rolled back:

const result = await orchestrator.syncSchemas(localModels, options);

if (!result.migrationResult?.success) {
  console.error('Migration had errors:', result.migrationResult?.errors);
  console.log(`Completed: ${result.migrationResult?.operationsCompleted}/${result.migrationResult?.totalOperations}`);
}

Confirmation Flow

When a migration plan requires confirmation (any warning or destructive operations), the behavior depends on the mode:

ModeBehavior
Interactive (default)Shows plan, asks for confirmation before executing
--dry-runShows plan, stops without executing
--forceShows plan, executes immediately without asking
Programmatic (skipConfirmation: true)Returns the plan without executing, lets caller decide

Next Steps

On this page