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.md
source 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

  1. Run ESLint before committing:
    npx eslint convex/
  2. Use auto-fix for quick migrations:
    npx eslint convex/ --fix
  3. Add to CI pipeline to catch violations early
  4. Configure your editor for real-time feedback

Quick Reference

RuleWhat it enforces
no-old-registered-function-syntax
Object syntax with
handler
require-argument-validators
args: {}
on all functions
explicit-table-ids
Table name in db operations
import-wrong-runtime
No Node imports in Convex runtime

References