
Function Creator
Scaffold Convex queries, mutations, and actions with validators, auth patterns, and error handling when adding backend endpoints.
Overview
Function Creator is an agent skill for the Build phase that creates Convex queries, mutations, and actions with validation, authentication, and error handling.
Install
npx skills add https://github.com/get-convex/agent-skills --skill function-creatorWhat is this skill?
- Separates query (read/cached), mutation (transactional write), and action (external/long-running) patterns
- TypeScript examples with convex/values validators and explicit returns typing
- Covers secure handler structure: args validation, authentication, and error handling
- Mutation snippet shows optional enums and ACID transaction semantics with automatic retries
- Use when implementing new API endpoints on an existing Convex project
- Three function types documented: query, mutation, action
Adoption & trust: 555 installs on skills.sh; 31 GitHub stars; 2/3 security scanners passed (skills.sh audits).
What problem does it solve?
You need a new Convex API endpoint but want type-safe validators, correct function type choice, and secure handlers without re-reading the docs each time.
Who is it for?
Indie devs and agents extending a Convex backend with additional CRUD, workflows, or external API actions.
Skip if: Greenfield schema design-only work (use schema-focused skills first) or non-Convex stacks.
When should I use this skill?
Creating new query, mutation, or action functions; adding API endpoints to your Convex backend.
What do I get? / Deliverables
You get production-oriented Convex function stubs ready to drop into convex/ with validated args, returns, and handler skeletons aligned to query/mutation/action rules.
- Convex query/mutation/action module snippets with validators
- Handler patterns including auth and error handling guidance
Recommended Skills
Journey fit
Function creation is core backend implementation during the build phase when the product data layer and API surface are defined. Backend subphase is where Convex server functions, schema validation, and transactional writes are authored.
How it compares
Convex-specific function scaffolding—not a generic REST OpenAPI generator or MCP database bridge.
Common Questions / FAQ
Who is function-creator for?
Developers using Convex with Claude Code, Cursor, or Codex who want consistent server function templates when shipping backend features.
When should I use function-creator?
During build/backend whenever you add a query, mutation, or action endpoint to your Convex app.
Is function-creator safe to install?
The skill guides code you write locally; review generated auth and data-access logic and check the Security Audits panel on this page for the parent package.
SKILL.md
READMESKILL.md - Function Creator
# Convex Function Creator Generate secure, type-safe Convex functions following all best practices. ## When to Use - Creating new query functions (read data) - Creating new mutation functions (write data) - Creating new action functions (external APIs, long-running) - Adding API endpoints to your Convex backend ## Function Types ### Queries (Read-Only) - Can only read from database - Cannot modify data or call external APIs - Cached and reactive - Run in transactions ```typescript import { query } from "./_generated/server"; import { v } from "convex/values"; export const getTask = query({ args: { taskId: v.id("tasks") }, returns: v.union(v.object({ _id: v.id("tasks"), text: v.string(), completed: v.boolean(), }), v.null()), handler: async (ctx, args) => { return await ctx.db.get(args.taskId); }, }); ``` ### Mutations (Transactional Writes) - Can read and write to database - Cannot call external APIs - Run in ACID transactions - Automatic retries on conflicts ```typescript import { mutation } from "./_generated/server"; import { v } from "convex/values"; export const createTask = mutation({ args: { text: v.string(), priority: v.optional(v.union( v.literal("low"), v.literal("medium"), v.literal("high") )), }, returns: v.id("tasks"), handler: async (ctx, args) => { const identity = await ctx.auth.getUserIdentity(); if (!identity) throw new Error("Not authenticated"); return await ctx.db.insert("tasks", { text: args.text, priority: args.priority ?? "medium", completed: false, createdAt: Date.now(), }); }, }); ``` ### Actions (External + Non-Transactional) - Can call external APIs (fetch, AI, etc.) - Can call mutations via `ctx.runMutation` - Cannot directly access database - No automatic retries - **Use `"use node"` directive when needing Node.js APIs** **Important:** If your action needs Node.js-specific APIs (crypto, third-party SDKs, etc.), add `"use node"` at the top of the file. Files with `"use node"` can ONLY contain actions, not queries or mutations. ```typescript "use node"; // Required for Node.js APIs like OpenAI SDK import { action } from "./_generated/server"; import { api } from "./_generated/api"; import { v } from "convex/values"; import OpenAI from "openai"; const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY }); export const generateTaskSuggestion = action({ args: { prompt: v.string() }, returns: v.string(), handler: async (ctx, args) => { const identity = await ctx.auth.getUserIdentity(); if (!identity) throw new Error("Not authenticated"); // Call OpenAI (requires "use node") const completion = await openai.chat.completions.create({ model: "gpt-4", messages: [{ role: "user", content: args.prompt }], }); const suggestion = completion.choices[0].message.content; // Write to database via mutation await ctx.runMutation(api.tasks.createTask, { text: suggestion, }); return suggestion; }, }); ``` **Note:** If you only need basic fetch (no Node.js APIs), you can omit `"use node"`. But for third-party SDKs, crypto, or other Node.js features, you must use it. ## Required Components ### 1. Argument Validation **Always** define `args` with validators: ```typescript args: { id: v.id("tasks"), text: v.string(), count: v.number(), enabled: v.boolean(), tags: v.array(v.string()), metadata: v.optional(v.object({ key: v.string(), })), } ``` ### 2. Return Type Validation **Always** define `returns`: ```typescript returns: v.object({ _id: v.id("tasks"), text: v.string(), }) // Or for arrays returns: v.array(v.object({ /* ... */ })) // Or for nullable returns: v.union(v.object({ /* ... */ }), v.null()) ```