1---2name: convex-core3description: Build Convex schemas, queries, mutations, actions, and client usage with strict validators and indexes. Use for data modeling, function authoring, argument/return validation, and performance guidance. Use proactively when work touches convex/schema.ts, functions, or api.* references. Examples: - user: "Design tables for multi-tenant app" → defineSchema/defineTable with indexes - user: "Write a mutation" → args/returns validators + auth checks - user: "Optimize query" → add index and withIndex range expression - user: "Use useQuery" → show generated hook usage4---5
6<overview>
7Core Convex modeling and function authoring patterns. This skill is the default baseline for Convex work.
8</overview>
9
10<reference>
11- **Schemas**: https://docs.convex.dev/database/schemas
12- **Reading data + indexes**: https://docs.convex.dev/database/reading-data/indexes
13- **Validation**: https://docs.convex.dev/functions/validation
14- **Functions**: https://docs.convex.dev/functions
15- **Pagination**: https://docs.convex.dev/database/pagination
16- **System tables**: https://docs.convex.dev/database/advanced/system-tables
17</reference>
18
19<context name="Core Concepts">
20- Schema: `defineSchema`/`defineTable` in `convex/schema.ts`; use `v` validators.
21- Options: `schemaValidation: false` disables runtime validation; `strictTableNameTypes: false` allows undeclared tables in TS types.
22- Validation: `args`/`returns` validators for queries/mutations/actions; objects reject extra props; `undefined` is invalid (use `null`).
23- Discriminated Unions: Use `v.union` and `v.literal` with `as const` for type-safe state/kind definitions.
24- Types: Use `v.int64()` for 64-bit integers (not `v.bigint()`); `v.null()` for explicit null returns.
25- IDs: Use `Id<"table">` and `v.id("table")` instead of raw strings.
26- Records: `v.record(keys, values)` keys MUST be ASCII, non-empty, and NOT start with `_` or `$`.
27- Validator composition: `Infer`, `.pick`, `.omit`, `.extend`, `.partial` on object validators.
28</context>
29
30<rules>
31
32### Schema Rules
33- You MUST define all tables in `convex/schema.ts` with `defineSchema` / `defineTable`.
34- System Fields: `_id` (`v.id(tableName)`) and `_creationTime` (`v.number()`) are added automatically.
35- You MUST use `v.*` validators for every field; SHOULD avoid `v.any()` unless necessary.
36- Index naming: include all fields in the name, e.g., `"by_field1_and_field2"`.
37</rules>
38- Index rules:
39 - You MUST use `.index(name, [fields...])`.
40 - Field order matters; range expressions MUST follow index order.
41 - Limits: 16 fields per index, 32 indexes per table.
42
43### Function Rules
44- You MUST use new function syntax with `args`, `returns`, and `handler`.
45- You MUST always validate `args` and `returns` (HTTP actions excluded).
46- Use `query` for reads, `mutation` for writes, and `action` for external/long-running.
47- Actions MUST NOT access `ctx.db`; You MUST use `ctx.runQuery` / `ctx.runMutation`.
48- Circular Dependencies: When calling a function in the same file via `ctx.run*`, You MUST add explicit return type annotations to the receiver variable.
49
50### Database Operations
51- You MUST provide the explicit table name as the first argument to `ctx.db.get`, `ctx.db.patch`, `ctx.db.replace`, and `ctx.db.delete`.
52- Replacement: Use `ctx.db.replace` for full document replacement (throws if missing).
53- Patching: Use `ctx.db.patch` for shallow merge updates (throws if missing).
54- Deletion: Convex queries do NOT support `.delete()`. You MUST `.collect()` results and iterate to call `ctx.db.delete(id)`.
55- Unique: Use `.unique()` for single document results; it MUST throw if multiple documents match.
56- You MUST NOT use `filter` in production queries; use indexes and `.withIndex`.
57
58### TypeScript Best Practices
59- You MUST use `as const` for string literals in discriminated unions.
60- You MUST define arrays as `const array: Array<T> = [...]` and records as `const record: Record<K, V> = {...}`.
61- You MUST prefer `Id<"table">` over `string` for all document identifiers.
62
63### Query Performance
64- You SHOULD prefer `.withIndex` over `.filter` on large tables.
65- If using `.withIndex` without a range, You MUST pair it with `take`, `first`, `unique`, or `paginate`.
66- Search limits: `collect()` throws if >1024 docs; You SHOULD use `take(n)`, `paginate()`, or `for await` iteration for large sets.
67- Async iteration: Use `for await (const row of query)` instead of `.collect()` for streaming large result sets.
68
69### Pagination
70- You MUST use `paginationOptsValidator` in args.
71- `.paginate()` returns `{ page, isDone, continueCursor }`.
72- Pages are reactive; size MAY change.
73- You SHOULD avoid strict `returns` validators for the full `.paginate()` result object; validate `page` or use `v.any()`.
74
75### Client Patterns
76- You MUST use `api.*` references from `convex/_generated/api`.
77- React hooks: `useQuery`, `useMutation`, `useAction`, `usePaginatedQuery`.
78- You MUST NOT call mutations or actions during render.
79
80### Safety
81- You MUST enforce auth per function; check identity via `ctx.auth` helpers.
82- You MUST NOT expose sensitive logic in public functions; MUST use internal ones.
83
84</rules>