LLM Skills
~/catalogue/backend//SKILL
Backendsource GitHub

Migrations convexes

/SKILL

Stratégies de migration des schémas pour les applications en évolution, y compris l'ajout de nouveaux champs, le remplissage des données, la suppression des champs obsolètes, les migrations d'index et

// contenu du skill

name: convex-migrations

displayName: Convex Migrations

description: Schema migration strategies for evolving applications including adding new fields, backfilling data, removing deprecated fields, index migrations, and zero-downtime migration patterns

version: 1.0.0

author: Convex

tags: [convex, migrations, schema, database, data-modeling]


Convex Migrations

Evolve your Convex database schema safely with patterns for adding fields, backfilling data, removing deprecated fields, and maintaining zero-downtime deployments.

Documentation Sources

Before implementing, do not assume; fetch the latest documentation:

  • Primary: https://docs.convex.dev/database/schemas
  • Schema Overview: https://docs.convex.dev/database
  • Migration Patterns: https://stack.convex.dev/migrate-data-postgres-to-convex
  • For broader context: https://docs.convex.dev/llms.txt

Instructions

Migration Philosophy

Convex handles schema evolution differently than traditional databases:

  • No explicit migration files or commands
  • Schema changes deploy instantly with npx convex dev
  • Existing data is not automatically transformed
  • Use optional fields and backfill mutations for safe migrations

Adding New Fields

Start with optional fields, then backfill:

typescript
// Step 1: Add optional field to schema
// convex/schema.ts
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";

export default defineSchema({
  users: defineTable({
    name: v.string(),
    email: v.string(),
    // New field - start as optional
    avatarUrl: v.optional(v.string()),
  }),
});
typescript
// Step 2: Update code to handle both cases
// convex/users.ts
import { query } from "./_generated/server";
import { v } from "convex/values";

export const getUser = query({
  args: { userId: v.id("users") },
  returns: v.union(
    v.object({
      _id: v.id("users"),
      name: v.string(),
      email: v.string(),
      avatarUrl: v.union(v.string(), v.null()),
    }),
    v.null()
  ),
  handler: async (ctx, args) => {
    const user = await ctx.db.get(args.userId);
    if (!user) return null;

    return {
      _id: user._id,
      name: user.name,
      email: user.email,
      // Handle missing field gracefully
      avatarUrl: user.avatarUrl ?? null,
    };
  },
});
typescript
// Step 3: Backfill existing documents
// convex/migrations.ts
import { internalMutation } from "./_generated/server";
import { internal } from "./_generated/api";
import { v } from "convex/values";

const BATCH_SIZE = 100;

export const backfillAvatarUrl = internalMutation({
  args: {
    cursor: v.optional(v.string()),
  },
  returns: v.object({
    processed: v.number(),
    hasMore: v.boolean(),
  }),
  handler: async (ctx, args) => {
    const result = await ctx.db
      .query("users")
      .paginate({ numItems: BATCH_SIZE, cursor: args.cursor ?? null });

    let processed = 0;
    for (const user of result.page) {
      // Only update if field is missing
      if (user.avatarUrl === undefined) {
        await ctx.db.patch(user._id, {
          avatarUrl: generateDefaultAvatar(user.name),
        });
        processed++;
      }
    }

    // Schedule next batch if needed
    if (!result.isDone) {
      await ctx.scheduler.runAfter(0, internal.migrations.backfillAvatarUrl, {
        cursor: result.continueCursor,
      });
    }

    return {
      processed,
      hasMore: !result.isDone,
    };
  },
});

function generateDefaultAvatar(name: string): string {
  return `https://api.dicebear.com/7.x/initials/svg?seed=${encodeURIComponent(name)}`;
}
typescript
// Step 4: After backfill completes, make field required
// convex/schema.ts
export default defineSchema({
  users: defineTable({
    name: v.string(),
    email: v.string(),
    avatarUrl: v.string(), // Now required
  }),
});

Removing Fields

Remove field usage before removing from schema:

typescript
// Step 1: Stop using the field in queries and mutations
// Mark as deprecated in code comments

// Step 2: Remove field from schema (make optional first if needed)
// convex/schema.ts
export default defineSchema({
  posts: defineTable({
    title: v.string(),
    content: v.string(),
    authorId: v.id("users"),
    // legacyField: v.optional(v.string()), // Remove this line
  }),
});

// Step 3: Optionally clean up existing data
// convex/migrations.ts
export const removeDeprecatedField = internalMutation({
  args: {
    cursor: v.optional(v.string()),
  },
  returns: v.null(),
  handler: async (ctx, args) => {
    const result = await ctx.db
      .query("posts")
      .paginate({ numItems: 100, cursor: args.cursor ?? null });

    for (const post of result.page) {
      // Use replace to remove the field entirely
      const { legacyField, ...rest } = post as typeof post & { legacyField?: string };
      if (legacyField !== undefined) {
        await ctx.db.replace(post._id, rest);
      }
    }

    if (!result.isDone) {
  
// source originale publique
strataga/claude-setup
/skills/convex-migrations/SKILL.md
Licence : Licence non indiquée. Consultez le dépôt avant toute réutilisation.
Projet indépendant, non affilié à Anthropic. Ce skill reste la propriété de son auteur original.
// installer ce skill
Collez cette commande dans votre terminal à la racine de votre projet :
mkdir -p .claude/commands && curl -o ".claude/commands/SKILL.md" "https://raw.githubusercontent.com/strataga/claude-setup/master/skills/convex-migrations/SKILL.md"
Ensuite dans Claude Code, tapez /SKILL pour l'activer.
open_in_newVoir la source originale
// sauvegarder
Sauvegarde disponible après connexion.
loginSe connecter pour sauvegarder
// informations
Créateurstrataga
CatégorieBackend
Format.md
AccèsGratuit
// similaires

Skills Backend

Voir toutarrow_forward