convex

Convex backend development - database, queries, mutations, actions, and real-time APIs

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "convex" with this command: npx skills add sebastiaanwouters/dotagents/sebastiaanwouters-dotagents-convex

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

TypeTS TypeValidatorNotes
Idstringv.id(tableName)
Nullnullv.null()Use instead of undefined
Int64bigintv.int64()NOT v.bigint() (deprecated)
Float64numberv.number()
Booleanbooleanv.boolean()
Stringstringv.string()
BytesArrayBufferv.bytes()
ArrayArrayv.array(values)Max 8192 values
ObjectObjectv.object({prop: value})Max 1024 entries
RecordRecordv.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.tsapi.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

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

simplify-code

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

svelte-code-writer

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

skill-from-github

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

code-review

No summary provided by upstream source.

Repository SourceNeeds Review