Design and generate Convex database schemas with proper validation, indexes, and relationships. Use when creating schema.ts or modifying table definitions.
Build well-structured Convex schemas following best practices for relationships, indexes, and validators.
convex/schema.ts filev.* typesimport { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";
export default defineSchema({
tableName: defineTable({
field: v.string(),
optional: v.optional(v.number()),
userId: v.id("users"),
status: v.union(
v.literal("active"),
v.literal("pending"),
v.literal("archived")
),
createdAt: v.number(),
updatedAt: v.optional(v.number()),
})
.index("by_user", ["userId"])
.index("by_user_and_status", ["userId", "status"])
.index("by_created", ["createdAt"]),
});
export default defineSchema({
users: defineTable({
name: v.string(),
email: v.string(),
}).index("by_email", ["email"]),
posts: defineTable({
userId: v.id("users"),
title: v.string(),
content: v.string(),
}).index("by_user", ["userId"]),
});
export default defineSchema({
users: defineTable({ name: v.string() }),
projects: defineTable({ name: v.string() }),
projectMembers: defineTable({
userId: v.id("users"),
projectId: v.id("projects"),
role: v.union(v.literal("owner"), v.literal("member")),
})
.index("by_user", ["userId"])
.index("by_project", ["projectId"])
.index("by_project_and_user", ["projectId", "userId"]),
});
export default defineSchema({
comments: defineTable({
postId: v.id("posts"),
parentId: v.optional(v.id("comments")),
userId: v.id("users"),
text: v.string(),
})
.index("by_post", ["postId"])
.index("by_parent", ["parentId"]),
});
v.string()
v.number()
v.boolean()
v.null()
v.id("tableName")
v.optional(v.string())
v.union(v.literal("a"), v.literal("b"))
v.object({ key: v.string(), nested: v.number() })
v.array(v.string())
v.record(v.string(), v.boolean())
v.any()
by_user: ["userId"])by_user_and_status: ["userId", "status"])by_a_and_b usually covers by_av.union(v.literal(...)) patternv.number() (milliseconds since epoch)