#What's the recommended approach for migrations?

5 messages · Page 1 of 1 (latest)

ivory heath
#

How would I for example add or remove a new required field in a table?

little pelicanBOT
#

Hi @ivory heath. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

Convex recommends online, asynchronous migrations: keep your app serving traffic, update the schema to allow both old and new data, backfill with a migration, then tighten the schema once the data is consistent. [Intro migrations; Migrations component]

Below are concrete patterns for adding and removing a required field.


Adding a new required field

Goal: add bar: v.string() to table foobar.

1. Make the field optional in the schema

First deploy a schema that allows existing documents without bar:

// schema.ts
foobar: defineTable({
  foo: v.string(),
  bar: v.optional(v.string()),
})

This lets you deploy even though existing docs don’t have bar. [Migrating with mutations]

Update your app code to:

  • Write bar for all new/updated documents.
  • Read bar defensively (it may be undefined until backfill is done).

2. Backfill existing documents with a migration

Use the Migrations component (or your own mutation) to set a default for all existing docs where bar is missing. [Migrations component; Data migrations]

Example with the component:

// convex/migrations.ts
import { Migrations } from "@convex-dev/migrations";
import { components } from "./_generated/api";
import type { DataModel } from "./_generated/dataModel";

export const migrations = new Migrations<DataModel>(components.migrations);
#

export const setDefaultBar = migrations.define({
table: "foobar",
migrateOne: async (ctx, doc) => {
if (doc.bar === undefined) {
await ctx.db.patch(doc._id, { bar: "something" });
}
},
});


Run this migration to completion. [[Migrations component](<https://www.convex.dev/components/migrations>)]

### 3. Make the field required

Once every document has `bar` set, change the schema to required:

```ts
// schema.ts
foobar: defineTable({
  foo: v.string(),
  bar: v.string(), // now required
})

Convex will only accept this schema once the data actually conforms, so your generated TypeScript types stay in sync with reality. [Migrating with mutations]


Removing a required field

Goal: remove isPro: v.boolean() from teams.

1. Make the field optional

Relax the schema first:

// schema.ts
teams: defineTable({
  // ...
  isPro: v.optional(v.boolean()),
})

Convex won’t let you remove a field that still has data; making it optional is the first step. [Best practices]

Update your code to stop relying on isPro (or treat it as optional / deprecated).

2. Clear the field with a migration

Run a migration that sets isPro to undefined everywhere. [Data migrations]

export const removeIsPro = migrations.define({
  table: "teams",
  migrateOne: async (ctx, team) => {
    if (team.isPro !== undefined) {
      await ctx.db.patch(team._id, { isPro: undefined });
    }
  },
});

3. Remove the field from the schema

After the data is cleared, you can safely delete the field from the schema:

#
// schema.ts
teams: defineTable({
  // ... no isPro field
})

Convex will now accept this schema because no documents still have isPro. [Best practices]


Tools you can use

If you describe your exact schema change, I can sketch the precise schema + migration code for it.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: