UNPKG

oneie

Version:

Build apps, websites, and AI agents in English. Zero-interaction setup for AI agents (Claude Code, Cursor, Windsurf). Download to your computer, run in the cloud, deploy to the edge. Open source and free forever.

131 lines (106 loc) 3.76 kB
--- title: Convex Mutation Pattern dimension: knowledge category: patterns tags: ai, backend, ontology related_dimensions: connections, events, groups, people, things scope: global created: 2025-11-03 updated: 2025-11-03 version: 1.0.0 ai_context: | This document is part of the knowledge dimension in the patterns category. Location: one/knowledge/patterns/backend/convex-mutation-pattern.md Purpose: Documents convex mutation pattern Related dimensions: connections, events, groups, people, things For AI agents: Read this to understand convex mutation pattern. --- # Convex Mutation Pattern **Category:** Backend **Type:** Data Modification **Used for:** Creating, updating, or deleting entities in the ontology --- ## Pattern Overview Every mutation in Convex should: 1. Validate inputs against ontology types 2. Perform the operation on the appropriate table 3. Create event record for audit trail 4. Return the result ## Code Template ```typescript import { mutation } from "./_generated/server"; import { v } from "convex/values"; export const create = mutation({ args: { type: v.string(), name: v.string(), properties: v.any(), }, handler: async (ctx, args) => { // 1. Get authenticated user (if required) const identity = await ctx.auth.getUserIdentity(); if (!identity) { throw new Error("Not authenticated"); } // 2. Validate against ontology // Check that type exists in ontology // Validate required fields based on type // 3. Validate identity has group const person = await ctx.db.query("people") .withIndex("by_email", q => q.eq("email", identity.email)) .first(); if (!person || !person.groupId) { throw new Error("User must belong to a group"); } // 4. Insert into things table const thingId = await ctx.db.insert("things", { type: args.type, name: args.name, properties: args.properties, groupId: person.groupId, status: "draft", createdAt: Date.now(), updatedAt: Date.now(), }); // 5. Log creation event await ctx.db.insert("events", { type: "entity_created", actorId: person._id, targetId: thingId, timestamp: Date.now(), metadata: { entityType: args.type, entityName: args.name, groupId: person.groupId, }, }); // 6. Return result return thingId; }, }); ``` ## When to Use - Creating new things (actors, content, products, tokens, etc.) - Creating connections between things - Recording events for the audit trail - Updating thing state or properties - Deleting things (soft delete via status = "archived") ## Best Practices 1. **Always authenticate** - Get user identity and validate they belong to a group 2. **Use groupId** - Scope all operations by groupId for multi-tenant isolation 3. **Always log events** - Every mutation should create an event record with actorId and targetId 4. **Use soft deletes** - Change status to "archived" instead of hard deleting 5. **Use canonical types** - Use 66 thing types from ontology, not custom types 6. **Return minimal data** - Return IDs only, let queries fetch full objects 7. **Use transactions** - Convex handles transactions automatically within a mutation ## Common Mistakes **Don't:** Skip event logging **Do:** Always create an event record **Don't:** Hard delete records **Do:** Use status field for soft deletes **Don't:** Return full objects with sensitive data **Do:** Return IDs and let queries fetch data ## Related Patterns - [Convex Query Pattern](./convex-query-pattern.md) - [Event Logging Pattern](./event-logging-pattern.md) - [Ontology Validation Pattern](./ontology-validation-pattern.md)