Convex
"Convex reactive backend expert: schema design, TypeScript functions, real-time subscriptions, auth, file storage, scheduling, and deployment."
$ npx claude-code-templates@latest --skill="development/convex" --yesRequires Claude Code. The command adds this skill to your project's .claudedirectory — nothing runs on ToolZip's servers.
What's inside this skill
Component source (preview)
Convex
You are an expert in Convex — the open-source, reactive backend platform where queries are TypeScript code. You have deep knowledge of schema design, function authoring (queries, mutations, actions), real-time data subscriptions, authentication, file storage, scheduling, and deployment workflows across React, Next.js, Angular, Vue, Svelte, React Native, and server-side environments.
When to Use
- Use when building a new project with Convex as the backend
- Use when adding Convex to an existing React, Next.js, Angular, Vue, Svelte, or React Native app
- Use when designing schemas for a Convex document-relational database
- Use when writing or debugging Convex functions (queries, mutations, actions)
- Use when implementing real-time/reactive data patterns
- Use when setting up authentication with Convex Auth or third-party providers (Clerk, Auth0, etc.)
- Use when working with Convex file storage, scheduled functions, or cron jobs
- Use when deploying or managing Convex projects
Core Concepts
Convex is a document-relational database with a fully managed backend. Key differentiators:
- Reactive by default: Queries automatically re-run and push updates to all connected clients when underlying data changes
- TypeScript-first: All backend logic — queries, mutations, actions, schemas — is written in TypeScript
- ACID transactions: Serializable isolation with optimistic concurrency control
- No infrastructure to manage: Serverless, scales automatically, zero config
- End-to-end type safety: Types flow from schema → backend functions → client hooks
Function Types
| Type | Purpose | Can Read DB | Can Write DB | Can Call External APIs | Cached/Reactive |
|---|---|---|---|---|---|
| Query | Read data | ✅ | ❌ | ❌ | ✅ |
| Mutation | Write data | ✅ | ✅ | ❌ | ❌ |
| Action | Side effects | via runQuery | via runMutation | ✅ | ❌ |
| HTTP Action | Webhooks/custom endpoints | via runQuery | via runMutation | ✅ | ❌ |
Project Setup
New Project (Next.js)
npx create-next-app@latest my-app
cd my-app && npm install convex
npx convex dev
Add to Existing Project
npm install convex
npx convex dev
The npx convex dev command:
- Prompts you to log in (GitHub)
- Creates a project and deployment
- Generates
convex/folder for backend functions - Syncs functions to your dev deployment in real-time
- Creates
.env.localwithCONVEX_DEPLOYMENTandNEXT_PUBLIC_CONVEX_URL
Folder Structure
my-app/
├── convex/
│ ├── _generated/ ← Auto-generated (DO NOT EDIT)
│ │ ├── api.d.ts
│ │ ├── dataModel.d.ts
│ │ └── server.d.ts
│ ├── schema.ts ← Database schema definition
│ ├── tasks.ts ← Query/mutation functions
│ └── http.ts ← HTTP actions (optional)
├── .env.local ← CONVEX_DEPLOYMENT, NEXT_PUBLIC_CONVEX_URL
└── convex.json ← Project config (optional)
Schema Design
Define your schema in convex/schema.ts using the validator library:
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";
export default defineSchema({
users: defineTable({
name: v.string(),
email: v.string(),
avatarUrl: v.optional(v.string()),
tokenIdentifier: v.string(),
})
.index("by_token", ["tokenIdentifier"])
.index("by_email", ["email"]),
messages: defineTable({
authorId: v.id("users"),
channelId: v.id("channels"),
body: v.string(),
attachmentId: v.optional(v.id("_storage")),
})
.index("by_channel", ["channelId"])
.searchIndex("search_body", { searchField: "body" }),
channels: defineTable({
name: v.string(),
description: v.optional(v.string()),
isPrivate: v.boolean(),
}),
});
Validator Types
| Validator | TypeScript Type | Notes |
|---|---|---|
v.string() | string | |
v.number() | number | IEEE 754 float |
v.bigint() | bigint | |
v.boolean() | boolean | |
v.null() | null | |
v.id("tableName") | Id<"tableName"> | Document reference |
v.array(v.string()) | string[] | |
v.object({...}) | {...} | Nested objects |
v.optional(v.string()) | string \ | undefined |
v.union(v.string(), v.number()) | string \ | number |
v.literal("active") | "active" | Literal types |
v.bytes() | ArrayBuffer | Binary data |
v.float64() | number | Explicit 64-bit float (used in vector indexes) |
v.any() | any | Escape hatch |
Indexes
// Single-field index
defineTable({ email: v.string() }).index("by_email", ["email"]);
// Compound index (order matters for range queries)
defineTable({
orgId: v.string(),
createdAt: v.number(),
}).index("by_org_and_date", ["orgId", "createdAt"]);
// Full-text search index
defineTable({ body: v.string(), channelId: v.id("channels") }).searchIndex(
"search_body",
{
searchField: "body",
filterFields: ["channelId"],
},
);
// Vector search index (for AI/embeddings)
defineTable({ embedding: v.array(v.float64()), text: v.string() }).vectorIndex(
"by_embedding",
{
vectorField: "embedding",
dimensions: 1536,
},
);
Writing Functions
Queries (Read Data)
Queries are reactive — clients automatically get updates when data changes.
import { query } from "./_generated/server";
import { v } from "convex/values";
// Simple query — list all tasks
export const list = query({
args: {},
handler: async (ctx) => {
return await ctx.db.query("tasks").collect();
},
});
// Query with arguments and filtering
export const getByChannel = query({
args: { channelId: v.id("channels") },
handler: async (ctx, args) => {
return await ctx.db
.query("messages")
.withIndex("by_channel", (q) => q.eq("channelId", args.channelId))
.order("desc")
.take(50);
},
});
// Query with auth check
export const getMyProfile = query({
args: {},
handler: async (ctx) => {
const identity = await ctx.auth.getUserIdentity();
if (!identity) return null;
return await ctx.db
.query("users")
.withIndex("by_token", (q) =>
q.eq("tokenIdentifier", identity.tokenIdentifier),
)
.unique();
},
});
### Paginated Queries
Use cursor-based pagination for lists or infinite scroll UIs.
typescript
import { query } from "./_generated/server";
import { paginationOptsValidator } from "convex/server";
export const listPaginated = query({
args: {
paginationOpts: paginationOptsValidator
},
handler: async (ctx, args) => {
return await ctx.db
.query("messages")
.order("desc")
.paginate(args.paginationOpts);
},
});
### Mutations (Write Data)
Mutations run as ACID transactions with serializable isolation.
typescript
import { mutation } from "./_generated/server";
import { v } from "convex/values";
// Insert a document
export const create = mutation({
args: { text: v.string(), isCompleted: v.boolean() },
handler: async (ctx, args) => {
const taskId = await ctx.db.insert("tasks", {
text: args.text,
isCompleted: args.isCompleted,
});
return taskId;
},
});
// Update a document
export const update = mutation({
args: { id: v.id("tasks"), isCompleted: v.boolean() },
handler: async (ctx, args) => {
await ctx.db.patch(args.id, { isCompleted: args.isCompleted });
},
});
// Delete a document
export const remove = mutation({
args: { id: v.id("tasks") },
handler: async (ctx, args) => {
await ctx.db.delete(args.id);
},
});
// Multi-document transaction (automatically atomic)
export const transferCredits = mutation({
args: {
fromUserId: v.id("users"),
toUserId: v.id("users"),
amount: v.number(),
},
handler: async (ctx, args) => {
const fromUser = await ctx.db.get(args.fromUserId);
const toUser = await ctx.db.get(args.toUserId);
if (!fromUser || !toUser) throw new Error("User not found");
if (fromUser.credits < args.amount) throw new Error("Insufficient credits");
await ctx.db.patch(args.fromUserId, {
credits: fromUser.credits - args.amount,
});
await ctx.db.patch(args.toUserId, {
credits: toUser.credits + args.amount,
});
},
});
### Actions (External APIs & Side Effects)
Actions can call third-party services but cannot directly access the database — they must use `ctx.runQuery` and `ctx.runMutation`.typescript
import { action } from "./_generated/server";
import { v } from "convex/values";
import { api } from "./_generated/api";
export const sendEmail = action({
args: { to: v.string(), subject: v.string(), body: v.string() },
handler: async (ctx, args) => {
// Call external API
const response = await fetch("https://api.sendgrid.com/v3/mail/send", {
method: "POST",
headers: {
Authorization: Bearer ${process.env.SENDGRID_API_KEY},
"Content-Type": "application/json",
},
body: JSON.stringify({
personalizations: [{ to: [{ email: args.to }] }],
from: { email: "noreply@example.com" },
subject: args.subject,
content: [{ type: "text/plain", value: args.body }],
}),
});
if (!response.ok) throw new Error("Failed to send email");
// Write result back to database via mutation
await ctx.runMutation(api.emails.recordSent, {
to: args.to,
subject: args.subject,
sentAt: Date.now(),
});
},
});
// Generate AI embeddings
export const generateEmbedding = action({
args: { text: v.string(), documentId: v.id("documents") },
handler: async (ctx, args) => {
const response = await fetch("https://api.openai.com/v1/embeddings", {
method: "POST",
headers: {
Authorization: Bearer ${process.env.OPENAI_API_KEY},
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "text-embedding-3-small",
input: args.text,
}),
});
const { data } = await response.json();
await ctx.runMutation(api.documents.saveEmbedding, {
documentId: args.documentId,
embedding: data[0].embedding,
});
},
});
### HTTP Actions (Webhooks)typescript
import { httpRouter } from "convex/server";
import { httpAction } from "./_generated/server";
import { api } from "./_generated/api";
const http = httpRouter();
http.route({
path: "/webhooks/stripe",
method: "POST",
handler: httpAction(async (ctx, request) => {
const body
Preview truncated. View the full source on GitHub →
Related Claude Code Skills
Code Reviewer
Comprehensive code review skill for TypeScript, JavaScript, Python, Swift, Kotlin, Go. Includes automated code analysis, best practice checking, security scanning, and review checklist generation. Use when reviewing pull requests, providing code feedback, identifying issues, or ensuring code quality standards.
Senior Frontend
Comprehensive frontend development skill for building modern, performant web applications using ReactJS, NextJS, TypeScript, Tailwind CSS. Includes component scaffolding, performance optimization, bundle analysis, and UI best practices. Use when developing frontend features, optimizing performance, implementing UI/UX designs, managing state, or reviewing frontend code.
Senior Backend
Comprehensive backend development skill for building scalable backend systems using NodeJS, Express, Go, Python, Postgres, GraphQL, REST APIs. Includes API scaffolding, database optimization, security implementation, and performance tuning. Use when designing APIs, optimizing database queries, implementing business logic, handling authentication/authorization, or reviewing backend code.
Senior Architect
Comprehensive software architecture skill for designing scalable, maintainable systems using ReactJS, NextJS, NodeJS, Express, React Native, Swift, Kotlin, Flutter, Postgres, GraphQL, Go, Python. Includes architecture diagram generation, system design patterns, tech stack decision frameworks, and dependency analysis. Use when designing system architecture, making technical decisions, creating architecture diagrams, evaluating trade-offs, or defining integration patterns.
Skill Creator
Create new skills, modify and improve existing skills, and measure skill performance. Use when users want to create a skill from scratch, edit, or optimize an existing skill, run evals to test a skill, benchmark skill performance with variance analysis, or optimize a skill's description for better triggering accuracy.
Senior Fullstack
Comprehensive fullstack development skill for building complete web applications with React, Next.js, Node.js, GraphQL, and PostgreSQL. Includes project scaffolding, code quality analysis, architecture patterns, and complete tech stack guidance. Use when building new projects, analyzing code quality, implementing design patterns, or setting up development workflows.
Catalog data and component content are sourced from the open-source davila7/claude-code-templates project (MIT license). ToolZip curates the listing and writes original descriptions; every component links back to its original source. Claude Code is a product of Anthropic. ToolZip is an independent catalog and is not affiliated with or endorsed by Anthropic.