Appearance
Migrate Data on Upgrade
Module tables are part of your app's database. When an ERP Kit release renames, removes, or tightens a field or table in a module your app uses, deploying the new version changes the schema, but it does not migrate the data already stored. ERP Kit does not ship automatic migrations or compatibility aliases. Your app migrates its own data with TailorDB migrations.
ERP Kit is at an early stage, so any release may include breaking changes to module tables. Check every upgrade with the update advisor as described below.
When you need this
You need a migration only for a workspace with data you want to keep, such as staging and production, and only when a release changes a table your app uses. A development workspace can instead be reset with pnpm run seed:reset. Optional additions, and changes to modules you do not use, need no migration.
Enable migrations
The app templates do not enable migrations. Enable them on the backend's TailorDB namespace before the first deploy to a workspace whose data you keep, so every later schema change is recorded. In apps/<app>/backend/tailor.config.ts:
typescript
export default defineConfig({
// ...
db: {
"main-db": {
files: [`./src/modules-db.ts`],
gqlOperations: "query",
migration: {
directory: "./migrations",
// Optional. Defaults to the first machine user in auth.machineUsers.
machineUser: "developer",
},
},
},
});Then create the baseline snapshot from the current tables, and commit the migrations/ directory:
bash
# in apps/<app>/backend
pnpm exec tailor tailordb migration generateThe migration machine user needs read and write access to every table the scripts touch. See Machine user and permissions.
If the app is already deployed and its schema matches your local tables, the next deploy only records the baseline. See Initial setup for other cases.
Generate the migration for an upgrade
The erp-kit-update-advisor skill finds the database changes between your installed version and the target. When the database your app uses has breaking changes, it also generates a migration scaffold in an isolated working copy, without changing your project. Run it before you upgrade, as part of Update packages:
/erp-kit-update-advisorThe report lists the breaking changes, the generated migration number and files, the data transformations still to write, and any open questions. It marks the result as "generated; implementation and verification required". A generated scaffold alone does not migrate any data.
To generate the migration yourself instead, upgrade the packages, then run from the backend:
bash
pnpm exec tailor tailordb migration generate --name "upgrade erp-kit to <version>"Do not use --init on a deployed project; it discards the history.
Write the transformation
Fill in each generated migrate.ts. The SDK guide covers the script structure, field renames, and table renames. For ERP data, also follow these rules:
- Keep what the books depend on. Preserve quantities, amounts, record IDs, references between records, and the history needed to reverse a posting.
- Do not replay history through module commands. Posting commands create inventory movements and journal entries; running them again double-posts. Transform the stored rows directly.
- Ask when the mapping is unclear. If a release changes what a value means and the mapping to the new value is a business decision, decide it with the business owner instead of guessing.
- Migrate stored values too. Renamed enum values, source-document discriminators, and permission scopes stored on users and roles need the same transformation as table and field names.
- Finish every
TODO. Do not deleteTODO(tailor-migration-review)markers or pass--no-scriptto hide unfinished work. Initial values for new required fields are not a substitute for a real transformation.
Deploy
pnpm run deploy runs pending migrations automatically, in order, before the rest of the deploy. For each one, it relaxes the breaking constraints, runs migrate.ts, then applies the final schema.
What happens when a script fails depends on how it is written:
- A single
mainfunction, or one step: it runs in one transaction. If it throws, its data changes roll back, the schema returns to the previous migration, and the next deploy runs it again. - Two or more
steps: each step runs in its own transaction. If a step fails after an earlier step completed, the completed steps stay applied and the migration stays in progress. The affected tables stay in maintenance mode, with GraphQL writes and record events disabled, until a later deploy finishes the remaining steps. Write every step so it can run again on rows it has already migrated.
See Splitting a migration into steps and Failure recovery.
Before deploying to staging or production:
- Pause writes to the app, or schedule the deploy for a quiet period.
- Keep a copy of the affected data that you can restore from. A migration checkpoint is not a data backup, and there is no automatic down-migration; see Rollback strategy.
- Deploy, then check what matters to the business against the real data, such as stock levels, open balances, and that every reference still resolves.