API Reference
Convex Functions
Server-side queries, mutations, and actions for operational data
Function Types
Convex provides three types of functions:
| Type | Purpose | Can Read DB | Can Write DB | Can Call External APIs |
|---|---|---|---|---|
| Query | Read data | ✅ | ❌ | ❌ |
| Mutation | Write data | ✅ | ✅ | ❌ |
| Action | External APIs | ✅ | ❌ (via mutations) | ✅ |
Organizations
Workflows
Tracked Queries
Access Control
All Convex functions enforce access control:
// Admins have access to ALL organizations in their company
const membership = await ctx.db
.query("company_members")
.withIndex("by_user_company", q =>
q.eq("userId", userId).eq("companyId", companyId)
)
.unique();
if (membership.role === "admin") {
// Full company access
}// Regular users must be in organization_access table
const access = await ctx.db
.query("organization_access")
.withIndex("by_company_org_user", q =>
q.eq("companyId", companyId)
.eq("organizationId", organizationId)
.eq("userId", userId)
)
.first();
if (!access) {
throw new Error("Access denied");
}Best Practices
Query Optimization
Always use indexes - Never use .filter() for lookups that can use indexes.
// ❌ BAD - Full table scan
const records = await ctx.db
.query("analytics_llm_responses")
.filter(q => q.eq(q.field("organizationId"), orgId))
.collect();
// ✅ GOOD - Index-based lookup
const records = await ctx.db
.query("analytics_llm_responses")
.withIndex("by_org_week", q =>
q.eq("organizationId", orgId)
.eq("weekNumber", weekNum)
)
.collect();Pagination
For large result sets, use pagination:
import { paginationOptsValidator } from "convex/server";
export const getResponses = query({
args: {
organizationId: v.id("organizations"),
paginationOpts: paginationOptsValidator
},
handler: async (ctx, args) => {
return await ctx.db
.query("analytics_llm_responses")
.withIndex("by_org", q => q.eq("organizationId", args.organizationId))
.paginate(args.paginationOpts);
}
});Error Handling
Convex functions throw errors that are serialized to the client:
try {
const result = await client.mutation(api.func, { args });
} catch (error) {
if (error.message.includes("Access denied")) {
// Handle permission error
} else if (error.message.includes("not found")) {
// Handle not found
}
}