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...
convex/schema.ts with defineSchema / defineTable._id (v.id(tableName)) and _creationTime (v.number()) are added automatically.v.* validators for every field; SHOULD avoid v.any() unless necessary."by_field1_and_field2"..index(name, [fields...]).args, returns, and handler.args and returns (HTTP actions excluded).query for reads, mutation for writes, and action for external/long-running.ctx.db; You MUST use ctx.runQuery / ctx.runMutation.ctx.run*, You MUST add explicit return type annotations to the receiver variable.ctx.db.get, ctx.db.patch, ctx.db.replace, and ctx.db.delete.ctx.db.replace for full document replacement (throws if missing).ctx.db.patch for shallow merge updates (throws if missing)..delete(). You MUST .collect() results and iterate to call ctx.db.delete(id)..unique() for single document results; it MUST throw if multiple documents match.filter in production queries; use indexes and .withIndex.as const for string literals in discriminated unions.const array: Array<T> = [...] and records as const record: Record<K, V> = {...}.Id<"table"> over string for all document identifiers..withIndex over .filter on large tables..withIndex without a range, You MUST pair it with take, first, unique, or paginate.collect() throws if >1024 docs; You SHOULD use take(n), paginate(), or for await iteration for large sets.for await (const row of query) instead of .collect() for streaming large result sets.paginationOptsValidator in args..paginate() returns { page, isDone, continueCursor }.returns validators for the full .paginate() result object; validate page or use v.any().api.* references from convex/_generated/api.useQuery, useMutation, useAction, usePaginatedQuery.ctx.auth helpers.