원클릭으로
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