Server API
Learn how to work with Convex backend functions and database.
Convex Functions
Convex provides three types of functions for your backend logic:
- Queries - Read data from the database
- Mutations - Write data to the database
- Actions - Perform external API calls or non-deterministic operations
Writing a Query
import { query } from "./_generated/server";
import { v } from "convex/values";
export const getUser = query({
args: { userId: v.id("users") },
handler: async (ctx, args) => {
return await ctx.db.get(args.userId);
},
});
Writing a Mutation
import { mutation } from "./_generated/server";
import { v } from "convex/values";
export const createTask = mutation({
args: { text: v.string() },
handler: async (ctx, args) => {
const taskId = await ctx.db.insert("tasks", {
text: args.text,
completed: false,
});
return taskId;
},
});
Authentication in Functions
Use the getAuthUserId
helper to get the authenticated user:
import { getAuthUserId } from "@convex-dev/auth/server";
export const myProtectedFunction = mutation({
handler: async (ctx) => {
const userId = await getAuthUserId(ctx);
if (!userId) {
throw new Error("Not authenticated");
}
// Function logic here
},
});
Database Schema
Define your database schema in convex/schema.ts
:
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";
export default defineSchema({
tasks: defineTable({
text: v.string(),
completed: v.boolean(),
userId: v.id("users"),
}).index("by_user", ["userId"]),
});
Best Practices
- Always validate function arguments using Convex validators
- Use indexes for efficient querying
- Implement proper authentication checks in protected functions
- Keep functions small and focused on a single responsibility
- Use transactions for atomic operations