Migrating Ents

Migrating Ents

You can use the provided migration helpers from convex-helpers to migrate data at scale. Read the Stack article (opens in a new tab) to learn more about the helpers, then follow the steps here to use them with Ents.

Install dependencies

Make sure you have the latest version of convex-helpers installed:

npm i convex-helpers@latest

Add the migrations table to your schema

Wrap the migrationsTable definition from convex-helpers in defineEntFromTable to make it compatible with Ents:

convex/schema.ts
import { v } from "convex/values";
import {
  defineEnt,
  defineEntFromTable,
  defineEntSchema,
  getEntDefinitions,
} from "convex-ents";
import { migrationsTable } from "convex-helpers/server/migrations";
 
const schema = defineEntSchema({
  migrations: defineEntFromTable(migrationsTable),
  // your other ent definitions...
});
 
export default schema;
 
export const entDefinitions = getEntDefinitions(schema);

Add a migrations file

Besides the standard setup, you can create a custom mutationCtx helper to use ctx.table inside of your migrations (if you don't already have one in functions.ts):

convex/migrations.ts
import { makeMigration } from "convex-helpers/server/migrations";
import { internalMutation, MutationCtx } from "./_generated/server";
 
const migration = makeMigration(internalMutation, {
  migrationTable: "migrations",
});
 
async function mutationCtx(baseCtx: MutationCtx) {
  return {
    ...ctx,
    table: entsTableFactory(ctx, entDefinitions),
    db: undefined,
  };
}
 
// In this file, or in another one if you have many migrations
export const simpleMigration = migration({
  table: "users",
  migrateOne: async (_, user) => ({
    name: user.name + " MEng.",
  }),
});
 
// Example of using `mutationCtx` to access `ctx.table`
export const migrationUsingCtxTable = migration({
  table: "users",
  migrateOne: async (baseCtx, doc) => {
    const ctx = await mutationCtx(baseCtx);
    const user = await ctx.table("users").getX(doc._id);
    const numMessages = (await user.edge("messages")).length;
    if (numMessages > 10) {
      await user.patch({ name: user.name + " Pro" });
    }
  },
  batchSize: 10,
});

Dry run your migration

You can execute the migration from code and from the CLI or the dashboard:

npx convex run migrations:myMigration '{"dryRun": true, "fn": "migrations:simpleMigration"}' # --prod

See the Stack article (opens in a new tab) for other ways of running your migrations.

Examples

There is an example migrations file here: migrations.ts (opens in a new tab).