Skip to main content

convex-reviewer

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).

Jump to install

Source facts

Repository
get-convex/convex-kimi-plugin
Last source activity
July 22, 2026 at 00:42
Detected SKILL.md language
English
Stars
1
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions ยท Read-only preview
name
convex-reviewer
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).
license
Apache-2.0
# 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 1. **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 2. **Authorization** - [ ] Functions verify resource ownership before reads/writes - [ ] No trusting client-provided user IDs - [ ] Team/organization access properly validated 3. **Validation** - [ ] All public functions have `args` validator - [ ] All functions have `returns` validator - [ ] Validators match actual data structure 4. **Internal Functions** - [ ] Scheduled functions target `internal.*` not `api.*` - [ ] `ctx.runMutation` and `ctx.runAction` use appropriate scopes ### Performance 1. **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_b` covers `by_a`) 2. **Data Loading** - [ ] Not using `.collect()` on unbounded queries - [ ] Batch operations for large datasets - [ ] Pagination implemented where needed 3. **Reactivity** - [ ] No `Date.now()` in query functions - [ ] Time-based queries use arguments or status fields - [ ] Queries are deterministic ### Schema Design 1. **Structure** - [ ] Flat documents with relationships via IDs - [ ] No deeply nested arrays of objects - [ ] Arrays limited to small, bounded collections (<8192) 2. **Types** - [ ] Proper validators for all fields - [ ] Enums use `v.union(v.literal(...))` pattern - [ ] Optional fields use `v.optional()` - [ ] Timestamps use `v.number()` (not strings) 3. **Relationships** - [ ] One-to-many using foreign keys with indexes - [ ] Many-to-many using junction tables - [ ] No circular references ### Code Quality 1. **Async Handling** - [ ] All promises are awaited - [ ] No floating promises - [ ] Proper error handling 2. **Organization** - [ ] Query/mutation wrappers are thin - [ ] Business logic in plain TypeScript functions - [ ] Reusable helpers extracted - [ ] Clear function names 3. **Type Safety** - [ ] Using generated types from `dataModel` - [ ] Type imports from `_generated/dataModel` - [ ] No `any` types unless necessary ### Common Anti-Patterns Flag these issues: #### โŒ Filter on Database Query ```typescript // Bad const user = await ctx.db .query("users") .filter(q => q.eq(q.field("email"), email)) .first(); ``` Should use index: ```typescript // Good const user = await ctx.db .query("users") .withIndex("by_email", q => q.eq("email", email)) .first(); ``` #### โŒ Date.now() in Query ```typescript // 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 ```typescript // 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: ```typescript // 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 ```typescript // 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 ```typescript // Bad await ctx.scheduler.runAfter(0, api.tasks.process, args); ``` Should use internal: ```typescript // Good await ctx.scheduler.runAfter(0, internal.tasks.process, args); ``` ## Review Process 1. **First Pass**: Check security (auth, validation, authorization) 2. **Second Pass**: Check performance (indexes, queries, reactivity) 3. **Third Pass**: Check code quality (organization, types, patterns) 4. **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 ```typescript // 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:** ```typescript 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 `userId` arg - users can only update themselves - Added auth check via `getCurrentUser()` - Added `returns` validator - Users automatically update their own profile
View on GitHub