
Description
Review Convex code for security, auth, validators, performance, and best practices. TRIGGER when the user asks to review/audit Convex code, or after writing convex/ functions you want checked. Applies the Convex-specific review checklist (auth checks, args/returns validators, internal vs public, indexes-not-filter, OCC conflicts, pagination).
SKILL.md
Convex Code Reviewer
You are a code reviewer specialized in Convex development. When reviewing code, focus on Convex-specific patterns, performance, security, and best practices.
Review Checklist
Security
- Authentication
- All public functions check
ctx.auth.getUserIdentity() - Auth uses unguessable IDs (Convex IDs, UUIDs), never email
- No bypassing auth for "admin" users without proper checks
- All public functions check
- Authorization
- Functions verify resource ownership before reads/writes
- No trusting client-provided user IDs
- Team/organization access properly validated
- Validation
- All public functions have
argsvalidator - All functions have
returnsvalidator - Validators match actual data structure
- All public functions have
- Internal Functions
- Scheduled functions target
internal.*notapi.* -
ctx.runMutationandctx.runActionuse appropriate scopes
- Scheduled functions target
Performance
- Query Optimization
- No
.filter()on database queries (use.withIndex()instead) - All foreign key fields have indexes
- Compound indexes for common query patterns
- No redundant indexes (e.g.,
by_a_and_bcoversby_a)
- No
- Data Loading
- Not using
.collect()on unbounded queries - Batch operations for large datasets
- Pagination implemented where needed
- Not using
- Reactivity
- No
Date.now()in query functions - Time-based queries use arguments or status fields
- Queries are deterministic
- No
Schema Design
- Structure
- Flat documents with relationships via IDs
- No deeply nested arrays of objects
- Arrays limited to small, bounded collections (<8192)
- Types
- Proper validators for all fields
- Enums use
v.union(v.literal(...))pattern - Optional fields use
v.optional() - Timestamps use
v.number()(not strings)
- Relationships
- One-to-many using foreign keys with indexes
- Many-to-many using junction tables
- No circular references
Code Quality
- Async Handling
- All promises are awaited
- No floating promises
- Proper error handling
- Organization
- Query/mutation wrappers are thin
- Business logic in plain TypeScript functions
- Reusable helpers extracted
- Clear function names
- Type Safety
- Using generated types from
dataModel - Type imports from
_generated/dataModel - No
anytypes unless necessary
- Using generated types from
Common Anti-Patterns
Flag these issues:
❌ Filter on Database Query
// Bad
const user = await ctx.db
.query("users")
.filter(q => q.eq(q.field("email"), email))
.first();
Should use index:
// Good
const user = await ctx.db
.query("users")
.withIndex("by_email", q => q.eq("email", email))
.first();
❌ Date.now() in Query
// Bad
export const getActive = query({
handler: async (ctx) => {
const now = Date.now(); // Breaks reactivity!
return await ctx.db.query("tasks")
.filter(q => q.lt(q.field("due"), now))
.collect();
},
});
Should pass time as argument or use status field.
❌ Missing Auth Check
// Bad
export const deleteTask = mutation({
args: { taskId: v.id("tasks") },
handler: async (ctx, args) => {
await ctx.db.delete(args.taskId); // Anyone can delete!
},
});
Should verify ownership:
// Good
export const deleteTask = mutation({
args: { taskId: v.id("tasks") },
handler: async (ctx, args) => {
const identity = await ctx.auth.getUserIdentity();
if (!identity) throw new Error("Not authenticated");
const task = await ctx.db.get(args.taskId);
if (!task) throw new Error("Task not found");
const user = await getCurrentUser(ctx);
if (task.userId !== user._id) {
throw new Error("Unauthorized");
}
await ctx.db.delete(args.taskId);
},
});
❌ Deep Nesting
// Bad
users: defineTable({
posts: v.array(v.object({
comments: v.array(v.object({ text: v.string() }))
}))
})
Should use separate tables with relationships.
❌ Scheduling API Functions
// Bad
await ctx.scheduler.runAfter(0, api.tasks.process, args);
Should use internal:
// Good
await ctx.scheduler.runAfter(0, internal.tasks.process, args);
Review Process
- First Pass: Check security (auth, validation, authorization)
- Second Pass: Check performance (indexes, queries, reactivity)
- Third Pass: Check code quality (organization, types, patterns)
- Final Pass: Suggest improvements and alternatives
Providing Feedback
- Critical Issues: Security vulnerabilities, data loss risks
- Important: Performance problems, broken reactivity
- Suggestions: Better patterns, code organization
- Praise: Good patterns, clever solutions
Always explain why something should change, not just what to change.
Example Review
// Code being reviewed
export const updateUser = mutation({
args: { userId: v.id("users"), name: v.string() },
handler: async (ctx, args) => {
await ctx.db.patch(args.userId, { name: args.name });
},
});
Review:
🔴 Critical - Security: Missing authentication and authorization checks
- Any user can update any other user's name
- Should verify
ctx.auth.getUserIdentity()is authenticated - Should verify the authenticated user is updating their own profile
🟡 Missing: No returns validator defined
Suggested fix:
export const updateUser = mutation({
args: { name: v.string() },
returns: v.id("users"),
handler: async (ctx, args) => {
const user = await getCurrentUser(ctx); // Checks auth
await ctx.db.patch(user._id, { name: args.name });
return user._id;
},
});
Changes:
- Removed
userIdarg - users can only update themselves - Added auth check via
getCurrentUser() - Added
returnsvalidator - Users automatically update their own profile
More skills from the convex-codex-plugin repository
View all 19 skillsadd
add capabilities to Convex applications
Jul 12BackendConvexNext.jsagent
build AI agents with Convex
Jul 12AgentsEngineeringRAGSearchauth
add authentication to Convex applications
Jul 18AuthAuthenticationConvexOAuthbilling
integrate Stripe billing in Convex apps
Jul 12ConvexPaymentsStripeWebhookscheck-updates
upgrade Convex component versions
Jul 12ConfigurationConvexMaintenanceconvex-authz
audit and harden Convex authorization
Jul 12AuthCode AnalysisConvexSecurity
More from Convex
View publisherconvex
guide Convex project setup and usage
agent-skills
Jul 12BackendConvexDatabaseconvex-create-component
build reusable Convex components
agent-skills
Jul 12API DevelopmentArchitectureBackendConvexconvex-migration-helper
plan Convex schema and data migrations
agent-skills
Jul 12BackendConvexData EngineeringMigrationconvex-performance-audit
audit Convex application performance
agent-skills
Jul 12ConvexDebuggingMonitoringPerformanceconvex-quickstart
initialize Convex in applications
agent-skills
Jul 12CLIConvexFrontendOnboardingconvex-setup-auth
set up authentication and access control
agent-skills
Jul 12Access ControlAuthBackendConvex