
Schema Builder
- 2 installs
- 627 repo stars
- Updated May 20, 2026
- waynesutton/markdown-site
Designs and generates Convex database schemas with proper validation, indexes, and document-relational relationships in schema.ts.
About
This skill builds well-structured Convex schemas following document-relational, indexing, and validator best practices. A developer uses it when creating or modifying schema.ts, adding tables, designing relationships, or optimizing indexes.
- Document-relational design with indexed foreign keys
- Limits arrays to small bounded collections
Schema Builder by the numbers
- 2 all-time installs (skills.sh)
- +1 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #743 of 911 Databases skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/waynesutton/markdown-site --skill schema-builderAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 2 |
|---|---|
| repo stars | ★ 627 |
| Last updated | May 20, 2026 |
| Repository | waynesutton/markdown-site ↗ |
What it does
Designs and generates Convex database schemas with proper validation, indexes, and document-relational relationships in schema.ts.
Files
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
1. Document-Relational: Use flat documents with ID references, not deep nesting 2. Index Foreign Keys: Always index fields used in lookups (userId, teamId, etc.) 3. Limit Arrays: Only use arrays for small, bounded collections (<8192 items) 4. 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({
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"]),
});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")),
userId: v.id("users"),
text: v.string(),
})
.index("by_post", ["postId"])
.index("by_parent", ["parentId"]),
});Validator Reference
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()Index Strategy
1. Single-field indexes: For simple lookups (by_user: ["userId"]) 2. Compound indexes: For filtered queries (by_user_and_status: ["userId", "status"]) 3. Remove redundant: by_a_and_b usually covers by_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)
Source: https://github.com/get-convex/convex-agent-plugins