convex-development-general

Convex Development General Skill

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "convex-development-general" with this command: npx skills add oimiragieo/agent-studio/oimiragieo-agent-studio-convex-development-general

Convex Development General Skill

Schema and Validators

  • Always define table schemas using defineTable(v.object({...})) in convex/schema.ts .

  • Use v.id("tableName") for cross-document references — never plain v.string() .

  • Omit _id and _creationTime from schema definitions — they are auto-generated system fields.

  • See https://docs.convex.dev/database/types for all available validator types.

Function Registration

  • Use new function syntax: query({ args: {}, returns: v.null(), handler: async (ctx, args) => {...} }) .

  • ALWAYS include both argument (args ) and return (returns ) validators; if nothing is returned, use returns: v.null() .

  • Use internalQuery /internalMutation /internalAction for private functions — never expose internal logic via public API.

  • Use httpAction with httpRouter for HTTP endpoints in convex/http.ts .

Index-First Query Patterns

  • Prefer .withIndex("by_field", (q) => q.eq("field", value)) over .filter((q) => q.eq(q.field("field"), value)) .

  • Add indexes to schema.ts using .index("name", ["field1", "field2"]) on defineTable .

  • Use .withSearchIndex for full-text search patterns.

  • Avoid full table scans with .collect() on large tables — use .paginate(opts) or .take(n) .

Queries vs Mutations vs Actions

  • query : read-only, reactive (subscriptions), runs in V8 sandbox.

  • mutation : database writes, transactional, runs in V8 sandbox.

  • action : can call external APIs / run Node.js, NOT transactional — minimize direct db access.

  • Use ctx.runQuery /ctx.runMutation for cross-function calls; avoid action-to-mutation loops that split transactions.

Await All Promises

  • Always await ctx.db.patch(...) , await ctx.scheduler.runAfter(...) , etc.

  • Enable no-floating-promises ESLint rule to catch un-awaited Convex calls.

Real-Time Subscriptions

  • Client-side useQuery hooks auto-subscribe and re-render on data changes — no manual onSnapshot wiring needed.

  • Keep query functions deterministic to maximize cache hit rate.

export default defineSchema({ messages: defineTable({ channel: v.id("channels"), // cross-doc ref body: v.string(), user: v.id("users"), // _id and _creationTime are auto-added — do NOT include them }) .index("by_channel", ["channel"]) .index("by_channel_user", ["channel", "user"]), });

// convex/messages.ts — correct query with index + return validator import { query } from "./_generated/server"; import { v } from "convex/values";

export const getByChannel = query({ args: { channelId: v.id("channels") }, returns: v.array(v.object({ _id: v.id("messages"), body: v.string() })), handler: async (ctx, args) => { return await ctx.db .query("messages") .withIndex("by_channel", (q) => q.eq("channel", args.channelId)) .take(50); // bounded — never unbounded .collect() in production }, });

// convex/messages.ts — internal mutation (not exposed publicly) import { internalMutation } from "./_generated/server";

export const deleteOldMessages = internalMutation({ args: { before: v.number() }, returns: v.null(), handler: async (ctx, args) => { const old = await ctx.db .query("messages") .withIndex("by_channel", (q) => q.lt("_creationTime", args.before)) .take(100); await Promise.all(old.map((msg) => ctx.db.delete(msg._id))); }, });

</examples>

Iron Laws

  1. ALWAYS define document schemas using Convex v validators — never rely on raw TypeScript types alone for runtime-enforced schema correctness.
  2. NEVER manually include _id or _creationTime fields in schema definitions — they are automatically generated system fields and specifying them causes runtime errors.
  3. ALWAYS use v.id("tableName") for cross-document references — never store foreign keys as plain strings, which bypasses Convex's referential integrity tools.
  4. NEVER perform direct database mutations from client-side code — all mutations must be defined as Convex mutation functions in the convex/ directory.
  5. ALWAYS add .withIndex(...) for filtered queries on non-trivial tables — never use .filter() as a substitute for a missing index on production data, and never use .collect() without a bound (take(n) or .paginate()) on large tables.

Anti-Patterns

Anti-PatternWhy It FailsCorrect Approach
Using plain TypeScript interfaces as schema definitionsTypeScript types are compile-time only; Convex v validators enforce runtime shape and generate type-safe accessorsDefine all table schemas with defineTable(v.object({...}))
Adding _id or _creationTime to defineTable schemasConvex rejects schemas that include system fields, causing runtime initialization errorsOmit system fields; access them via doc._id and doc._creationTime after query
Storing cross-document references as plain v.string()Loses Convex's cross-reference validation and type inference for joined queriesUse v.id("tableName") so Convex validates the reference type
Running .collect() on large tables without paginationReturns all documents, causing memory spikes and timeouts on large datasetsUse .paginate(opts) or .take(100) with cursor-based pagination
Writing to the database from React client code directlyBypasses access control, validation, and audit trail; creates untraceable mutationsAll writes must go through a Convex mutation function in convex/
Using .filter() instead of .withIndex() for field-based lookups.filter() performs a full table scan; identical performance to filtering in-code but misses index speed-upDefine a schema index and use .withIndex(name, q => q.eq(...))

Memory Protocol (MANDATORY)

Before starting:

cat .claude/context/memory/learnings.md

After completing: Record any new patterns or exceptions discovered.

ASSUME INTERRUPTION: Your context may reset. If it's not in memory, it didn't happen.

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

pyqt6-ui-development-rules

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

code-analyzer

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

gcloud-cli

No summary provided by upstream source.

Repository SourceNeeds Review