schema-builder
Design and generate Convex database schemas with proper validation, indexes, and relationships. Use when creating schema.ts or modifying table definitions.
- License 许可
- AGPL-3.0
- 所属仓库
- baisharg/ASTN
- 最近更新
- 2026-11-13
- Stars
- ★ 2
- Forks
- 2
01 前言 Front Matter
- name
- schema-builder
- description
- Design and generate Convex database schemas with proper validation, indexes, and relationships. Use when creating schema.ts or modifying table definitions.
02 资源文件 1
- 参考文档bmad-skill-manifest.yaml1.0 KB
03 SKILL.md 正文
Convex Schema Builder
Build well-structured Convex schemas following best practices for relationships, indexes, and validators.
When to Use
- Creating a new
convex/schema.tsfile - Adding tables to existing schema
- Designing data model relationships
- Adding or optimizing indexes
- Converting nested data to relational structure
Schema Design Principles
- Document-Relational: Use flat documents with ID references, not deep nesting
- Index Foreign Keys: Always index fields used in lookups (userId, teamId, etc.)
- Limit Arrays: Only use arrays for small, bounded collections (<8192 items)
- Type Safety: Use strict validators with
v.*types
Schema Template
import { defineSchema, defineTable } from 'convex/server'
import { v } from 'convex/values'
export default defineSchema({
tableName: defineTable({
// Required fields
field: v.string(),
// Optional fields
optional: v.optional(v.number()),
// Relations (use IDs)
userId: v.id('users'),
// Enums with union + literal
status: v.union(
v.literal('active'),
v.literal('pending'),
v.literal('archived'),
),
// Timestamps
createdAt: v.number(),
updatedAt: v.optional(v.number()),
})
// Index for queries by this field
.index('by_user', ['userId'])
// Compound index for common query patterns
.index('by_user_and_status', ['userId', 'status'])
// Index for time-based queries
.index('by_created', ['createdAt']),
})
Common Patterns
One-to-Many Relationship
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']),
})
Many-to-Many with Junction Table
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']),
})
Hierarchical Data
export default defineSchema({
comments: defineTable({
postId: v.id('posts'),
parentId: v.optional(v.id('comments')), // null for top-level
userId: v.id('users'),
text: v.string(),
})
.index('by_post', ['postId'])
.index('by_parent', ['parentId']),
})
Small Bounded Arrays (OK to use)
export default defineSchema({
users: defineTable({
name: v.string(),
// Small, bounded collections are fine
roles: v.array(
v.union(v.literal('admin'), v.literal('editor'), v.literal('viewer')),
),
tags: v.array(v.string()), // e.g., max 10 tags
}),
})
Validator Reference
// Primitives
v.string()
v.number()
v.boolean()
v.null()
v.id('tableName')
// Optional
v.optional(v.string())
// Union types (enums)
v.union(v.literal('a'), v.literal('b'))
// Objects
v.object({
key: v.string(),
nested: v.number(),
})
// Arrays
v.array(v.string())
// Records (arbitrary keys)
v.record(v.string(), v.boolean())
// Any (avoid if possible)
v.any()
Index Strategy
-
Single-field indexes: For simple lookups
by_user: ["userId"]by_email: ["email"]
-
Compound indexes: For filtered queries
by_user_and_status: ["userId", "status"]by_team_and_created: ["teamId", "createdAt"]
-
Remove redundant:
by_a_and_busually coversby_a
Checklist
- [ ] All foreign keys have indexes
- [ ] Common query patterns have compound indexes
- [ ] Arrays are small and bounded (or converted to relations)
- [ ] All fields have proper validators
- [ ] Enums use
v.union(v.literal(...))pattern - [ ] Timestamps use
v.number()(milliseconds since epoch)
Migration from Nested to Relational
If converting from nested structures:
Before:
users: defineTable({
posts: v.array(
v.object({
title: v.string(),
comments: v.array(v.object({ text: v.string() })),
}),
),
})
After:
users: defineTable({
name: v.string(),
}),
posts: defineTable({
userId: v.id("users"),
title: v.string(),
}).index("by_user", ["userId"]),
comments: defineTable({
postId: v.id("posts"),
text: v.string(),
}).index("by_post", ["postId"]),
04 同仓库更多 Skills
integration-tanstack-start
PostHog integration for TanStack Start full-stack applications
migration-helper
Plan and execute Convex schema migrations safely, including adding fields, creating tables, and data transformations. Use when schema changes affect existing data.
gsd:sprint
Autonomous phase execution with Codex validation. Runs plan/execute/verify cycles in a bash loop until done or halted.
gsd:second-opinion
Get a second opinion from OpenAI Codex (GPT-5.2-codex) on code, architecture, or implementation decisions. Use when you want independent verification or an alternative perspective.
gsd:milestone-sprint
Run entire milestone autonomously with Codex validation. Auto-detects current milestone, executes all phases, and runs audit.
gsd:health-check
Verify GSD setup - checks Claude, Codex, skills, and MCP servers are working correctly.