convex

📁 sebastiaanwouters/dotagents 📅 Jan 21, 2026
8
总安装量
8
周安装量
#34462
全站排名
安装命令
npx skills add https://github.com/sebastiaanwouters/dotagents --skill convex

Agent 安装分布

claude-code 5
opencode 3
gemini-cli 3
github-copilot 3
codex 3
antigravity 2

Skill 文档

Convex Guidelines

Function Syntax

ALWAYS use the new function syntax with args, returns, and handler:

import { query } from "./_generated/server";
import { v } from "convex/values";

export const f = query({
  args: { name: v.string() },
  returns: v.string(),
  handler: async (ctx, args) => {
    return "Hello " + args.name;
  },
});

HTTP Endpoints

import { httpRouter } from "convex/server";
import { httpAction } from "./_generated/server";

const http = httpRouter();
http.route({
  path: "/echo",
  method: "POST",
  handler: httpAction(async (ctx, req) => {
    const body = await req.bytes();
    return new Response(body, { status: 200 });
  }),
});

Validators

Type TS Type Validator Notes
Id string v.id(tableName)
Null null v.null() Use instead of undefined
Int64 bigint v.int64() NOT v.bigint() (deprecated)
Float64 number v.number()
Boolean boolean v.boolean()
String string v.string()
Bytes ArrayBuffer v.bytes()
Array Array v.array(values) Max 8192 values
Object Object v.object({prop: value}) Max 1024 entries
Record Record v.record(keys, values) Keys: ASCII, nonempty, no $ or _ prefix

Discriminated Unions

v.union(
  v.object({ kind: v.literal("error"), errorMessage: v.string() }),
  v.object({ kind: v.literal("success"), value: v.number() }),
)

Function Registration

  • Public: query, mutation, action – exposed to internet
  • Internal: internalQuery, internalMutation, internalAction – only callable by other Convex functions

ALWAYS include argument AND return validators. Use returns: v.null() if no return value.

Function Calling

  • ctx.runQuery – call query from query/mutation/action
  • ctx.runMutation – call mutation from mutation/action
  • ctx.runAction – call action from action (only for crossing runtimes V8↔Node)

Use FunctionReference (from api or internal objects), NOT the function directly:

// Correct
await ctx.runQuery(api.example.f, { name: "Bob" });
// Wrong
await ctx.runQuery(f, { name: "Bob" });

For same-file calls, add type annotation to avoid circularity:

const result: string = await ctx.runQuery(api.example.f, { name: "Bob" });

Function References

  • api.path.to.functionName – public functions
  • internal.path.to.functionName – internal functions

File-based routing: convex/messages/access.ts → api.messages.access.functionName

Schema

Define in convex/schema.ts:

import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";

export default defineSchema({
  messages: defineTable({
    channelId: v.id("channels"),
    content: v.string(),
  }).index("by_channel", ["channelId"]),
});

System fields auto-added: _id (v.id(tableName)), _creationTime (v.number())

Index naming: include all fields → by_field1_and_field2

Queries

  • Do NOT use filter() – use withIndex() instead
  • No .delete() – collect results, iterate, call ctx.db.delete(row._id)
  • .unique() for single document (throws if multiple match)
  • Default order: ascending _creationTime
const messages = await ctx.db
  .query("messages")
  .withIndex("by_channel", (q) => q.eq("channelId", channelId))
  .order("desc")
  .take(10);

Full Text Search

const messages = await ctx.db
  .query("messages")
  .withSearchIndex("search_body", (q) =>
    q.search("body", "hello hi").eq("channel", "#general"),
  )
  .take(10);

Pagination

import { paginationOptsValidator } from "convex/server";

export const list = query({
  args: { paginationOpts: paginationOptsValidator, author: v.string() },
  handler: async (ctx, args) => {
    return await ctx.db
      .query("messages")
      .withIndex("by_author", (q) => q.eq("author", args.author))
      .order("desc")
      .paginate(args.paginationOpts);
  },
});

Returns: { page, isDone, continueCursor }

Mutations

  • ctx.db.patch(id, fields) – shallow merge
  • ctx.db.replace(id, doc) – full replace

Actions

Add "use node"; at top for Node.js modules. Actions have NO ctx.db access.

"use node";
import { action } from "./_generated/server";

export const myAction = action({
  args: {},
  returns: v.null(),
  handler: async (ctx, args) => {
    // Use ctx.runQuery/ctx.runMutation for DB access
    return null;
  },
});

Scheduling

Crons

import { cronJobs } from "convex/server";
import { internal } from "./_generated/api";

const crons = cronJobs();
crons.interval("job name", { hours: 2 }, internal.crons.myFunc, {});
export default crons;

Only use crons.interval or crons.cron. NOT crons.hourly/daily/weekly.

File Storage

const url = await ctx.storage.getUrl(storageId); // Returns signed URL or null

// Get metadata via system table
const metadata = await ctx.db.system.get(storageId);
// Returns: { _id, _creationTime, contentType?, sha256, size }

TypeScript

  • Use Id<'tableName'> from ./_generated/dataModel for typed IDs
  • as const for string literals in unions
  • Add @types/node to package.json when using Node.js modules
import { Id } from "./_generated/dataModel";
const idToName: Record<Id<"users">, string> = {};

Examples