Convex Code Review
Security Checklist
1. Argument AND Return Validators
- • All public
query,mutation,actionhaveargsvalidators - • All functions have
returnsvalidators - • No
v.any()for sensitive data - • HTTP actions validate request body (Zod recommended)
Search: query({, mutation({, action({ - check each has args: AND returns:
2. Error Handling
- • Uses
ConvexErrorfor user-facing errors (not plainError) - • Error codes are structured:
{ code: "NOT_FOUND", message: "..." } - • No sensitive info leaked in error messages
Search: throw new Error should be throw new ConvexError
3. Access Control
- • All public functions check
ctx.auth.getUserIdentity()where needed - • Uses auth helpers (
requireAuth,requireRole) - • No client-provided email/username for authorization
- • Row-level access verified (ownership checks)
Search: ctx.auth.getUserIdentity should appear in most public functions
4. Internal Functions
- •
ctx.runQuery,ctx.runMutation,ctx.runActionuseinternal.*notapi.* - •
ctx.scheduler.runAfterusesinternal.*notapi.* - • Crons in
crons.tsuseinternal.*notapi.*
Search: api. in convex directory - should not be used for scheduling/running
5. Table Names in DB Calls
- • All
ctx.db.get,patch,replace,deleteinclude table name as first arg
Search: db.get(, db.patch( - first arg should be quoted string
Performance Checklist
6. Database Queries
- • No
.filter()on queries (use.withIndex()or filter in code) - •
.collect()only with bounded results (<1000 docs) - • Pagination for large result sets
Search: \.filter\(\(?q, \.collect\(
7. Indexes
- • No redundant indexes (
by_foo+by_foo_and_bar) - • All filtered queries use
.withIndex() - • Index names include all fields
Review: schema.ts index definitions
8. Date.now() in Queries
- • No
Date.now()in query functions - • Time filtering uses boolean fields or client-passed timestamps
9. Promise Handling
- • All promises awaited (
ctx.scheduler,ctx.db.*)
ESLint: no-floating-promises
Architecture Checklist
10. Action Usage
- • Actions have
"use node";if using Node.js APIs - •
ctx.runActiononly when switching runtimes - • No sequential
ctx.runMutation/ctx.runQuery(combine for consistency)
11. Code Organization
- • Business logic in helper functions (
convex/model/) - • Public API handlers are thin wrappers
- • Auth helpers in
convex/lib/auth.ts
12. Transaction Consistency
- • Related reads in same query/mutation
- • Batch operations in single mutation
- • Mutations are idempotent
Quick Regex Searches
| Issue | Regex | Fix |
|---|---|---|
.filter() | \.filter\(\(?q | Use .withIndex() |
| Missing returns | handler:.*async without returns: | Add returns: |
| Plain Error | throw new Error\( | Use ConvexError |
| Missing table name | db\.(get|patch)\([^"'] | Add table name |
Date.now() in query | Date\.now\(\) | Remove from queries |
api.* scheduling | api\.[a-z] | Use internal.* |
Production Readiness
- •Security: Validators + ConvexError + Auth checks + Internal functions
- •Performance: Indexes + Bounded queries + No Date.now()
- •Architecture: Helper functions + Proper action usage + "use node"
- •Code Quality: Awaited promises + Table names + Return validators