Schema Rules
- •You MUST define all tables in
convex/schema.tswithdefineSchema/defineTable. - •System Fields:
_id(v.id(tableName)) and_creationTime(v.number()) are added automatically. - •You MUST use
v.*validators for every field; SHOULD avoidv.any()unless necessary. - •Index naming: include all fields in the name, e.g.,
"by_field1_and_field2". </rules> - •Index rules:
- •You MUST use
.index(name, [fields...]). - •Field order matters; range expressions MUST follow index order.
- •Limits: 16 fields per index, 32 indexes per table.
- •You MUST use
Function Rules
- •You MUST use new function syntax with
args,returns, andhandler. - •You MUST always validate
argsandreturns(HTTP actions excluded). - •Use
queryfor reads,mutationfor writes, andactionfor external/long-running. - •Actions MUST NOT access
ctx.db; You MUST usectx.runQuery/ctx.runMutation. - •Circular Dependencies: When calling a function in the same file via
ctx.run*, You MUST add explicit return type annotations to the receiver variable.
Database Operations
- •You MUST provide the explicit table name as the first argument to
ctx.db.get,ctx.db.patch,ctx.db.replace, andctx.db.delete. - •Replacement: Use
ctx.db.replacefor full document replacement (throws if missing). - •Patching: Use
ctx.db.patchfor shallow merge updates (throws if missing). - •Deletion: Convex queries do NOT support
.delete(). You MUST.collect()results and iterate to callctx.db.delete(id). - •Unique: Use
.unique()for single document results; it MUST throw if multiple documents match. - •You MUST NOT use
filterin production queries; use indexes and.withIndex.
TypeScript Best Practices
- •You MUST use
as constfor string literals in discriminated unions. - •You MUST define arrays as
const array: Array<T> = [...]and records asconst record: Record<K, V> = {...}. - •You MUST prefer
Id<"table">overstringfor all document identifiers.
Query Performance
- •You SHOULD prefer
.withIndexover.filteron large tables. - •If using
.withIndexwithout a range, You MUST pair it withtake,first,unique, orpaginate. - •Search limits:
collect()throws if >1024 docs; You SHOULD usetake(n),paginate(), orfor awaititeration for large sets. - •Async iteration: Use
for await (const row of query)instead of.collect()for streaming large result sets.
Pagination
- •You MUST use
paginationOptsValidatorin args. - •
.paginate()returns{ page, isDone, continueCursor }. - •Pages are reactive; size MAY change.
- •You SHOULD avoid strict
returnsvalidators for the full.paginate()result object; validatepageor usev.any().
Client Patterns
- •You MUST use
api.*references fromconvex/_generated/api. - •React hooks:
useQuery,useMutation,useAction,usePaginatedQuery. - •You MUST NOT call mutations or actions during render.
Safety
- •You MUST enforce auth per function; check identity via
ctx.authhelpers. - •You MUST NOT expose sensitive logic in public functions; MUST use internal ones.