Skip to main content
When you change a field name, type, or model structure, the data already on a user’s device still reflects the old shape. Migrations let you open that existing data with the old schemas, convert it to the current shape, and continue with the new generated API — all transparently at app startup.

How Cindel Runs Migrations

Cindel stores a database-level migration version inside every database file. When you open the database, CindelMigrationPlan compares the stored version with targetVersion, runs any missing steps in order, persists each successful toVersion after the step completes, and returns a database opened with the target schemas. Completed steps are skipped on future opens; only the outstanding steps run.

Core Types

CindelMigrationPlan

Declare a plan with a targetVersion, a baselineVersion (for databases that have no stored version yet), and an ordered list of steps:
Pass the same plan every time you open the database. Steps that have already been applied are skipped automatically.

CindelMigrationStep

Each step describes exactly one version move and carries its own schemas, migration logic, and optional verification hooks:

Step Lifecycle

Every migration step follows the same ordered lifecycle:
1

Export old data

Use context.exportObjects(schema) to read old typed objects, or context.exportDocuments(schema) to read raw map-shaped documents.
Both APIs read data in id order with bounded batches, so the process is predictable regardless of collection size.
2

Register target schemas

Call context.registerTargetSchemas() to prepare the target collections before you write any converted data into them.
Do not import target objects before this call.
3

Import converted data

Use context.importObjects(schema, iterable) for new typed objects, or context.importDocuments(schema, iterable) for target documents.
Make sure required fields are filled, removed fields are no longer referenced, and ids are preserved when records should remain the same logical objects.
4

Verify before and after (optional)

The verifyBefore hook runs before data is rewritten. The verifyAfter hook runs after. Throw from either hook to abort the step and prevent it from being marked complete.
Useful checks: required collections exist, document counts match expectations, ids were preserved, and converted values are valid for the new model.

Complete Example: Renaming a Field

This example migrates a User model from version 1 to version 2, where the fullName field is renamed to name and the email_address persisted field is normalised to email.

Automatic Compaction

Set compactOnSuccess: true on your CindelMigrationPlan to have Cindel call compact() automatically after a migration finishes. This reclaims storage space freed by replacing old records and is the preferred way to compact after a migration.

Best Practices

  • Keep steps small and single-purpose. One step should handle exactly one version move. For two releases of schema changes, write two steps — not one step that tries to handle every old shape.
  • Preserve dbIds. When existing records should remain the same logical objects after migration, copy old.dbId into the new object during conversion.
  • Always pass the migration plan at startup. Leave the plan in place until you are certain that no supported device can have a database below targetVersion. Removing the plan prematurely leaves some users unable to open their database.
  • Choose your export format deliberately. Use exportObjects when the old schema still produces useful Dart objects. Use exportDocuments when fields are being renamed heavily and you need direct access to persisted field names.
Do not remove the CindelMigrationPlan from Cindel.open() until every supported database is already at targetVersion. Removing it early means users still running an older database version will open without migration, potentially causing a crash or data corruption.

Low-Level Primitives

migrationVersion(), setMigrationVersion, registerMigratedSchemas, and compact() are low-level primitives exposed for controlled tooling and diagnostics. Do not call them directly in application migration code — use CindelMigrationPlan and CindelMigrationStep instead.