Skip to main content

function-creator

Create Convex queries, mutations, and actions with proper validation, authentication, and error handling. Use when implementing new API endpoints.

Jump to install

Source facts

Repository
waynesutton/markdown-site
Last source activity
March 21, 2026 at 00:13
Detected SKILL.md language
English
Stars
630
Forks
91

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
function-creator
description
Create Convex queries, mutations, and actions with proper validation, authentication, and error handling. Use when implementing new API endpoints.
# 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 ```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 - **Use `"use node"` directive when needing Node.js APIs** ```typescript "use node"; 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"); const completion = await openai.chat.completions.create({ model: "gpt-4", messages: [{ role: "user", content: args.prompt }], }); const suggestion = completion.choices[0].message.content; await ctx.runMutation(api.tasks.createTask, { text: suggestion }); return suggestion; }, }); ``` **Note:** Keep queries and mutations in files without `"use node"`. Actions that need Node.js go in separate files with `"use node"`. ## Required Components ### 1. Argument Validation **Always** define `args` with validators. ### 2. Return Type Validation **Always** define `returns`. ### 3. Authentication Check **Always** verify auth in public functions: ```typescript const identity = await ctx.auth.getUserIdentity(); if (!identity) throw new Error("Not authenticated"); ``` ### 4. Authorization Check **Always** verify ownership/permissions: ```typescript const task = await ctx.db.get(args.taskId); if (!task) throw new Error("Task not found"); if (task.userId !== user._id) throw new Error("Unauthorized"); ``` ## Internal Functions For backend-only functions (called by scheduler, other functions): ```typescript import { internalMutation } from "./_generated/server"; export const processExpiredTasks = internalMutation({ args: {}, handler: async (ctx) => { const now = Date.now(); const expired = await ctx.db .query("tasks") .withIndex("by_due_date", q => q.lt("dueDate", now)) .collect(); for (const task of expired) { await ctx.db.patch(task._id, { status: "expired" }); } }, }); ``` ## Checklist - [ ] `args` defined with validators - [ ] `returns` defined with validator - [ ] Authentication check (`ctx.auth.getUserIdentity()`) - [ ] Authorization check (ownership/permissions) - [ ] All promises awaited - [ ] Indexed queries (no `.filter()` on queries) - [ ] Error handling with descriptive messages - [ ] Scheduled functions use `internal.*` not `api.*` - [ ] If using Node.js APIs: `"use node"` at top of file - [ ] If file has `"use node"`: Only actions (no queries/mutations) Source: https://github.com/get-convex/convex-agent-plugins
View on GitHub