Appearance
Schema Migrations
Increment schemaVersion when you change your schema. Share the version between client and server via a plain constant file (avoids bundling Zod on the client):
app-schema-version.ts ← export const MY_SCHEMA_VERSION = 2;
schema.ts ← uses MY_SCHEMA_VERSION in defineSchema()
client-config.ts ← uses MY_SCHEMA_VERSION in defineClientConfig()When versions mismatch without a migration, the client clears local data, fires onSchemaVersionMismatch, and disposes.
Defining a Migration
Provide migrations for each version step:
typescript
import { defineMigration } from "poe-apps-sdk/v1/backend.js";
const migration1to2 = defineMigration(v1Mutators, v2Mutators, {
migrateData: async (ctx) => {
// Transform existing data
const items = await ctx.table("todos").scan().entries().toArray();
for (const [key, value] of items) {
await ctx.table("todos").set({
itemKey: key.itemKey,
value: { ...value, priority: 0 },
});
}
},
migratePendingMutation: {
// Transform in-flight mutations from old clients
addTodo: (args, emit) => {
emit("addTodo", { ...args, priority: 0 });
},
},
});
const schema = defineSchema({
schemaVersion: 2,
migrations: { "1to2": migration1to2 },
// ...
});Pending Mutation Handlers
Each mutation handler in migratePendingMutation can:
- Transform args — call
emit()with modified input - Rename — emit a different mutation name
- Drop — don't call
emit() - Expand — call
emit()multiple times to produce several mutations from one
Testing Migrations
Write a test that seeds data at the old schema version, bumps to the new one, and asserts both the migrated data and pending-mutation replays land correctly. See packages/synced-store-backend/protocol/migrations/ for the enforcement code.