Claude-skill-registry convex-eslint
Write Convex code that passes @convex-dev/eslint-plugin rules by default
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/convex-eslint" ~/.claude/skills/majiayu000-claude-skill-registry-convex-eslint && rm -rf "$T"
manifest:
skills/data/convex-eslint/SKILL.mdsource content
Convex ESLint Compliance
Write all Convex functions to pass @convex-dev/eslint-plugin. These rules prevent common bugs, security issues, and ensure code quality.
Documentation Sources
Setup
Install the plugin:
npm i @convex-dev/eslint-plugin --save-dev
Configure
eslint.config.js:
import { defineConfig } from "eslint/config"; import convexPlugin from "@convex-dev/eslint-plugin"; export default defineConfig([...convexPlugin.configs.recommended]);
Rules
1. no-old-registered-function-syntax
Always use object syntax with a
handler property.
// Correct export const list = query({ args: {}, handler: async (ctx) => { return await ctx.db.query("messages").collect(); }, }); // Wrong - bare function syntax export const list = query(async (ctx) => { return await ctx.db.query("messages").collect(); });
2. require-argument-validators
Always include
args object, even when empty.
// Correct - with arguments export const get = query({ args: { id: v.id("messages") }, handler: async (ctx, { id }) => { return await ctx.db.get("messages", id); }, }); // Correct - no arguments export const listAll = query({ args: {}, handler: async (ctx) => { return await ctx.db.query("messages").collect(); }, }); // Wrong - missing args export const get = query({ handler: async (ctx, { id }: { id: Id<"messages"> }) => { return await ctx.db.get("messages", id); }, });
3. explicit-table-ids
Use explicit table names in all database operations (Convex 1.31.0+).
// Correct const message = await ctx.db.get("messages", messageId); await ctx.db.patch("messages", messageId, { text: "updated" }); await ctx.db.replace("messages", messageId, { text: "replaced", author: "Alice", }); await ctx.db.delete("messages", messageId); // Wrong - implicit table from ID type const message = await ctx.db.get(messageId); await ctx.db.patch(messageId, { text: "updated" }); await ctx.db.replace(messageId, { text: "replaced", author: "Alice" }); await ctx.db.delete(messageId);
Migration codemod available:
npx @convex-dev/codemod@latest explicit-ids
4. import-wrong-runtime
Never import Node.js runtime files into Convex runtime files.
// convex/queries.ts (no "use node" directive) // Correct - importing from Convex runtime file import { helper } from "./utils"; // utils.ts has no "use node" // Wrong - importing from Node runtime file import { nodeHelper } from "./nodeUtils"; // nodeUtils.ts has "use node"
Best Practices
- Run ESLint before committing:
npx eslint convex/ - Use auto-fix for quick migrations:
npx eslint convex/ --fix - Add to CI pipeline to catch violations early
- Configure your editor for real-time feedback
Quick Reference
| Rule | What it enforces |
|---|---|
| Object syntax with |
| on all functions |
| Table name in db operations |
| No Node imports in Convex runtime |