一键导入
source-command-convex-schema-builder
Design Convex database schemas with proper validation, indexes, and relationship patterns
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Design Convex database schemas with proper validation, indexes, and relationship patterns
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Set up Convex authentication with proper user management, identity mapping, and access control patterns. Use when implementing auth flows.
Guide to using Convex components for feature encapsulation. Learn about sibling components, creating your own, and when to use components vs monolithic code.
Discover and use convex-helpers utilities for relationships, filtering, sessions, custom functions, and more. Use when you need pre-built Convex patterns.
Create Convex queries, mutations, and actions with proper validation, authentication, and error handling. Use when implementing new API endpoints.
Plan and execute Convex schema migrations safely, including adding fields, creating tables, and data transformations. Use when schema changes affect existing data.
Design and generate Convex database schemas with proper validation, indexes, and relationships. Use when creating schema.ts or modifying table definitions.
| name | source-command-convex-schema-builder |
| description | Design Convex database schemas with proper validation, indexes, and relationship patterns |
Use this skill when the user asks to run the migrated source command convex-schema-builder.
Design schemas for the Convex document-relational database. Schemas are defined in convex/schema.ts.
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";
export default defineSchema({
tableName: defineTable({
field: v.string(),
}).index("by_field", ["field"]),
});
// Primitives
v.string(); // string
v.number(); // number (use for timestamps, floats)
v.boolean(); // boolean
v.int64(); // 64-bit integer (NOT v.bigint())
v.null(); // null
v.bytes(); // ArrayBuffer
// Complex
v.id("tableName"); // Document ID reference
v.array(v.string()); // Array of strings
v.object({ key: v.string() }); // Typed object
v.optional(v.string()); // Optional field
v.union(v.literal("a"), v.literal("b")); // Enum / union type
v.record(v.string(), v.number()); // Dynamic keys (map)
v.any(); // Any type (avoid if possible)
From convex-helpers/validators:
import { nullable, literals, partial } from "convex-helpers/validators";
nullable(v.string()); // v.union(v.string(), v.null())
literals("a", "b", "c"); // v.union(v.literal("a"), ...)
partial(myObjectValidator); // All fields become optional
Store the foreign key on the "many" side with an index:
// One user has many tasks
users: defineTable({
name: v.string(),
email: v.string(),
}),
tasks: defineTable({
userId: v.id("users"), // Foreign key
title: v.string(),
completed: v.boolean(),
})
.index("by_userId", ["userId"]),
users: defineTable({
name: v.string(),
}),
teams: defineTable({
name: v.string(),
}),
// Junction table
teamMembers: defineTable({
userId: v.id("users"),
teamId: v.id("teams"),
role: v.union(v.literal("member"), v.literal("admin")),
joinedAt: v.number(),
})
.index("by_userId", ["userId"])
.index("by_teamId", ["teamId"])
.index("by_teamId_and_userId", ["teamId", "userId"]),
categories: defineTable({
name: v.string(),
parentId: v.optional(v.id("categories")), // Null for root
depth: v.number(),
})
.index("by_parentId", ["parentId"]),
Rules:
by_fieldA_and_fieldBby_userId_and_status covers queries on just userId)Common patterns:
tasks: defineTable({
userId: v.id("users"),
status: v.union(v.literal("active"), v.literal("completed")),
priority: v.number(),
createdAt: v.number(),
})
// Query tasks by user
.index("by_userId", ["userId"])
// Query tasks by user AND status (also covers by_userId queries)
.index("by_userId_and_status", ["userId", "status"])
// Query tasks by status across all users
.index("by_status", ["status"]),
Using indexes in queries:
// Single field
await ctx.db
.query("tasks")
.withIndex("by_userId", (q) => q.eq("userId", userId))
.collect();
// Compound index
await ctx.db
.query("tasks")
.withIndex("by_userId_and_status", (q) =>
q.eq("userId", userId).eq("status", "active"),
)
.collect();
// Range query on last index field
await ctx.db
.query("tasks")
.withIndex("by_userId_and_status", (q) =>
q.eq("userId", userId).gte("status", "a"),
)
.collect();
v.number() with Date.now(), not date stringsv.union(v.literal(...)) patternv.optional() for fields that may not existBad - deeply nested:
users: defineTable({
posts: v.array(
v.object({
comments: v.array(
v.object({
text: v.string(),
replies: v.array(v.object({ text: v.string() })),
}),
),
}),
),
});
Good - flat with relationships:
users: defineTable({ name: v.string() }),
posts: defineTable({ userId: v.id("users"), text: v.string() })
.index("by_userId", ["userId"]),
comments: defineTable({ postId: v.id("posts"), userId: v.id("users"), text: v.string() })
.index("by_postId", ["postId"]),
convex/schema.tsby_fieldA_and_fieldBv.int64() not v.bigint()npx convex codegen after changes