Skills Plugins MCP Prompt Model 博客 我的中心

convex-migrations

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

DeepseekModel 官方收录技能 质量 优秀 · 78 v1.0.0

获取

https://deepseekmodel.com/api/download.php?id=waynesutton-convexskills-skills-convex-migrations-skill-md&format=skill
下载 .skill 标准格式,含 system_prompt 与 model_config,导入任意 Agent 框架即可使用
.skill 文件中 system_prompt 字段的实际内容。
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: // 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 ()), }), }); // 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 , }; }, }); // 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)} ` ; } // 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: // 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 ) { await ctx. scheduler . runAfter ( 0 , internal. migrations . removeDeprecatedField , { cursor : result. continueCursor , }); } return null ; }, }); Renaming Fields Renaming requires copying data to new field, then removing old: // Step 1: Add new field as optional // convex/schema.ts export default defineSchema ({ users : defineTable ({ userName : v. string (), // Old field displayName : v. optional (v. string ()), // New field }), }); // Step 2: Update code to read from new field with fallback export const getUser = query ({ args : { userId : v. id ( "users" ) }, returns : v. object ({ _id : v. id ( "users" ), displayName : v. string (), }), handler : async (ctx, args) => { const user = await ctx. db . get (args. userId ); if (!user) throw new Error ( "User not found" ); return { _id : user. _id , // Read new field, fall back to old displayName : user. displayName ?? user. userName , }; }, }); // Step 3: Backfill to copy data export const backfillDisplayName = internalMutation ({ args : { cursor : v. optional (v. string ()) }, returns : v. null (), handler : async (ctx, args) => { const result = await ctx. db . query ( "users" ) . paginate ({ numItems : 100 , cursor : args. cursor ?? null }); for ( const user of result. page ) { if (user. displayName === undefined ) { await ctx. db . patch (user. _id , { displayName : user. userName , }); } } if (!result. isDone ) { await ctx. scheduler . runAfter ( 0 , internal. migrations . backfillDisplayName , { cursor : result. continueCursor , }); } return null ; }, }); // Step 4: After backfill, update schema to make new field required // and remove old field export default defineSchema ({ users : defineTable ({ // userName removed displayName : v. string (), }), }); Adding Indexes Add indexes before using them in queries: // Step 1: Add index to schema // convex/schema.ts export default defineSchema ({ posts : defineTable ({ title : v. string (), authorId : v. id ( "users" ), publishedAt : v. optional (v. number ()), status : v. string (), }) . index ( "by_author" , [ "authorId" ]) // New index . index ( "by_status_and_published" , [ "status" , "publishedAt" ]), }); // Step 2: Deploy schema change // Run: npx convex dev // Step 3: Now use the index in queries export const getPublishedPosts = query ({ args : {}, returns : v. array (v. object ({ _id : v. id ( "posts" ), title : v. string (), publishedAt : v. number (), })), handler : async (ctx) => { const posts = await ctx. db . query ( "posts" ) . withIndex ( "by_status_and_published" , ( q ) => q. eq ( "status" , "published" ) ) . order ( "desc" ) . take ( 10 ); return posts . filter ( ( p ) => p. publishedAt !== undefined ) . map ( ( p ) => ({ _id : p. _id , title : p. title , publishedAt : p. publishedAt !, })); }, }); Changing Field Types Type changes require careful migration: // Example: Change from string to number for a "priority" field // Step 1: Add new field with new type // convex/schema.ts export default defineSchema ({ tasks : defineTable ({ title : v. string (), priority : v. string (), // Old: "low", "medium", "high" priorityLevel : v. optional (v. number ()), // New: 1, 2, 3 }), }); // Step 2: Backfill with type conversion export const migratePriorityToNumber = internalMutation ({ args : { cursor : v. optional (v. string ()) }, returns : v. null (), handler : async (ctx, args) => { const result = await ctx. db . query ( "tasks" ) . paginate ({ numItems : 100 , cursor : args. cursor ?? null }); const priorityMap : Record < string , number > = { low : 1 , medium : 2 , high : 3 , }; for ( const task of result. page ) { if (task. priorityLevel === undefined ) { await ctx. db . patch (task. _id , { priorityLevel : priorityMap[task. priority ] ?? 1 , }); } } if (!result. isDone ) { await ctx. scheduler . runAfter ( 0 , internal. migrations . migratePriorityToNumber , { cursor : result. continueCursor , }); } return null ; }, }); // Step 3: Update code to use new field export const getTask = query ({ args : { taskId : v. id ( "tasks" ) }, returns : v. object ({ _id : v. id ( "tasks" ), title : v. string (), priorityLevel : v. number (), }), handler : async (ctx, args) => { const task = await ctx. db . get (args. taskId ); if (!task) throw new Error ( "Task not found" ); const priorityMap : Record < string , number > = { low : 1 , medium : 2 , high : 3 , }; return { _id : task. _id , title : task. title , priorityLevel : task. priorityLevel ?? priorityMap[task. priority ] ?? 1 , }; }, }); // Step 4: After backfill, update schema export default defineSchema ({ tasks : defineTable ({ title : v. string (), // priority field removed priorityLevel : v. number (), }), }); Migration Runner Pattern Create a reusable migration system: // convex/schema.ts import { defineSchema, defineTable } from "convex/server" ; import { v } from "convex/values" ; export default defineSchema ({ migrations : defineTable ({ name : v. string (), startedAt : v. number (), completedAt : v. optional (v. number ()), status : v. union ( v. literal ( "running" ), v. literal ( "completed" ), v. literal ( "failed" ) ), error : v. optional (v. string ()), processed : v. number (), }). index ( "by_name" , [ "name" ]), // Your other tables... }); // convex/migrations.ts import { internalMutation, internalQuery } from "./_generated/server" ;
Agent 识别该技能的关键词,点击任意一个即可复制。

该技能未提供触发词。

下载的 .skill 包内含以下字段。
字段 说明
format格式标识(skill/v1)
skill_id技能唯一 ID
name技能名称
version版本号
description技能描述
category所属分类(数组)
trigger_words触发词列表
tags标签列表
source来源标识
source_url来源链接(本页地址)
exported_at导出时间(每次下载生成)
system_prompt系统提示词正文
model_config模型参数:provider / model / temperature / max_tokens / top_p
examples示例
install_guide各平台导入说明(Coze / Dify / Claude / 自定义框架)
同一份技能可按不同平台格式导出。
.skill 标准格式,含 system_prompt 与 model_config,导入任意 Agent 框架即可使用 下载
.skillpro 增强格式,额外含脚本 / 工具 / 依赖 / 钩子占位 下载
.json 纯 JSON 导出,只含 system_prompt 与模型参数 下载
Coze 带 frontmatter 的 Markdown,Coze 平台导入用 下载
Dify Dify DSL,创建应用后直接导入 下载

每日精选 Skill 推荐,免费送到你邮箱

输入邮箱,每天接收一个精选 AI Agent 技能推荐。完全免费,持续更新。

提交后我们会发送一封确认邮件,点击邮件里的链接才会开始收信。

完全免费,取消任意时间。我们不会发送垃圾邮件。