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.

729 lines (570 loc) 15.9 kB
--- title: Backend Api Reference dimension: knowledge category: backend-api-reference.md tags: ai, backend, convex, groups, ontology related_dimensions: 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 backend-api-reference.md category. Location: one/knowledge/backend-api-reference.md Purpose: Documents backend api reference - quick guide Related dimensions: events, groups, people, things For AI agents: Read this to understand backend api reference. --- # Backend API Reference - Quick Guide **Status:** Production-Ready **Last Updated:** 2025-10-24 **Version:** 1.0.0 --- ## Overview Complete backend API for the 6-dimension ontology. All endpoints follow multi-tenant isolation (scoped to groupId). --- ## Groups API ### Queries ```typescript // Get group by slug (URL routing) api.queries.groups.getBySlug({ slug: "acme" }) // Get group by ID api.queries.groups.getById({ groupId }) // List groups with filters api.queries.groups.list({ type?: "organization" | "business" | "community" | "dao" | "government" | "friend_circle", status?: "active" | "archived", limit?: number }) // Get direct children api.queries.groups.getSubgroups({ parentGroupId }) // Get entire hierarchy (recursive) api.queries.groups.getHierarchy({ rootGroupId, maxDepth?: number // Default: 10 }) // Get breadcrumb trail api.queries.groups.getGroupPath({ groupId }) // Check if group is descendant api.queries.groups.isDescendantOf({ groupId, ancestorGroupId }) // Get entities in hierarchy api.queries.groups.getEntitiesInHierarchy({ rootGroupId, entityType?: string, limit?: number }) // Get statistics api.queries.groups.getStats({ groupId, includeSubgroups?: boolean }) // Search groups api.queries.groups.search({ query: string, type?: string, visibility?: "public" | "private", limit?: number }) ``` ### Mutations ```typescript // Create group api.mutations.groups.create({ slug: string, name: string, type: "organization" | "business" | "community" | "dao" | "government" | "friend_circle", parentGroupId?: Id<"groups">, description?: string, metadata?: any, settings?: { visibility: "public" | "private", joinPolicy: "open" | "invite_only" | "approval_required", plan?: "starter" | "pro" | "enterprise", limits?: { users: number, storage: number, apiCalls: number } } }) // Update group api.mutations.groups.update({ groupId: Id<"groups">, name?: string, description?: string, metadata?: any, settings?: { ... } }) // Archive group (soft delete) api.mutations.groups.archive({ groupId }) // Restore archived group api.mutations.groups.restore({ groupId }) ``` --- ## Things (Entities) API ### Queries ```typescript // List entities with filters api.queries.entities.list({ groupId: Id<"groups">, type?: string, // Any THING_TYPE from ontology status?: "draft" | "active" | "published" | "archived" | "inactive", limit?: number }) // Get entity by ID api.queries.entities.getById({ entityId: Id<"entities">, groupId?: Id<"groups"> // Optional security validation }) // Search entities by name api.queries.entities.search({ groupId: Id<"groups">, query: string, type?: string, limit?: number }) // Get entity with connections api.queries.entities.getWithConnections({ entityId: Id<"entities">, groupId?: Id<"groups"> }) // Returns: { entity, connectionsFrom, connectionsTo } // Get entity activity timeline api.queries.entities.getActivity({ entityId: Id<"entities">, limit?: number }) // Count entities by type api.queries.entities.countByType({ groupId }) // Returns: { [type: string]: number } // Count entities by status api.queries.entities.countByStatus({ groupId: Id<"groups">, type?: string }) // Returns: { [status: string]: number } // Get recent entities api.queries.entities.getRecent({ groupId: Id<"groups">, type?: string, limit?: number }) // Get recently updated api.queries.entities.getRecentlyUpdated({ groupId: Id<"groups">, type?: string, limit?: number }) ``` ### Mutations ```typescript // Create entity api.mutations.entities.create({ groupId: Id<"groups">, type: string, // Must be valid THING_TYPE name: string, properties?: any, status?: "draft" | "active" | "published" | "archived" | "inactive" }) // Update entity api.mutations.entities.update({ entityId: Id<"entities">, name?: string, properties?: any, status?: "draft" | "active" | "published" | "archived" | "inactive" }) // Archive entity (soft delete) api.mutations.entities.archive({ entityId }) // Restore archived entity api.mutations.entities.restore({ entityId }) ``` --- ## Connections API ### Queries ```typescript // List connections FROM an entity api.queries.connections.listFrom({ groupId: Id<"groups">, fromEntityId: Id<"entities">, relationshipType?: string // 25 connection types }) // List connections TO an entity api.queries.connections.listTo({ groupId: Id<"groups">, toEntityId: Id<"entities">, relationshipType?: string }) // List connections between two entities api.queries.connections.listBetween({ groupId: Id<"groups">, fromEntityId: Id<"entities">, toEntityId: Id<"entities">, relationshipType?: string }) // List connections by type api.queries.connections.listByType({ groupId: Id<"groups">, relationshipType: string, limit?: number }) ``` ### Mutations ```typescript // Create connection api.mutations.connections.create({ groupId: Id<"groups">, fromEntityId: Id<"entities">, toEntityId: Id<"entities">, relationshipType: string, // 25 types: owns, created_by, clone_of, etc. metadata?: any, strength?: number, validFrom?: number, validTo?: number }) // Upsert connection (create or update) api.mutations.connections.upsert({ groupId: Id<"groups">, fromEntityId: Id<"entities">, toEntityId: Id<"entities">, relationshipType: string, metadata?: any, strength?: number, validFrom?: number, validTo?: number }) // Bulk create connections api.mutations.connections.bulkCreate({ groupId: Id<"groups">, connections: Array<{ fromEntityId: Id<"entities">, toEntityId: Id<"entities">, relationshipType: string, metadata?: any }> }) ``` --- ## Events API (Read-Only) ### Queries ```typescript // List events (timeline view) api.queries.events.list({ groupId: Id<"groups">, eventType?: string, // 67 event types limit?: number, offset?: number }) // Events by actor (person who performed action) api.queries.events.byActor({ groupId: Id<"groups">, actorId: Id<"entities">, eventType?: string, limit?: number }) // Events by target (thing that was affected) api.queries.events.byTarget({ groupId: Id<"groups">, targetId: Id<"entities">, eventType?: string, limit?: number }) // Events by time range api.queries.events.byTimeRange({ groupId: Id<"groups">, startTime: number, endTime: number, eventType?: string, limit?: number }) // Event statistics api.queries.events.stats({ groupId: Id<"groups">, startTime?: number, endTime?: number }) // Returns: { total, byType, byActor, topTypes, topActors, timeRange } // Recent events api.queries.events.recent({ groupId: Id<"groups">, limit?: number // Default: 20 }) // Get event by ID api.queries.events.getById({ eventId: Id<"events">, groupId?: Id<"groups"> }) ``` --- ## Knowledge API (Read-Only) ### Queries ```typescript // List knowledge items api.queries.knowledge.list({ groupId: Id<"groups">, knowledgeType?: "label" | "document" | "chunk" | "vector_only", limit?: number }) // Search knowledge (basic text search) api.queries.knowledge.search({ groupId: Id<"groups">, query: string, knowledgeType?: "label" | "document" | "chunk" | "vector_only", limit?: number }) // Knowledge by source thing api.queries.knowledge.bySourceThing({ groupId: Id<"groups">, sourceThingId: Id<"entities">, knowledgeType?: "label" | "document" | "chunk" | "vector_only" }) // Knowledge linked to thing (via junction) api.queries.knowledge.byThing({ thingId: Id<"entities">, role?: "label" | "summary" | "chunk_of" | "caption" | "keyword" }) // Knowledge by label api.queries.knowledge.byLabel({ groupId: Id<"groups">, label: string, limit?: number }) // List all labels api.queries.knowledge.listLabels({ groupId }) // Returns: Array<{ label: string, count: number }> // Knowledge statistics api.queries.knowledge.stats({ groupId }) // Returns: { total, byType, withEmbeddings, withLabels, uniqueLabels } // Get knowledge by ID api.queries.knowledge.getById({ knowledgeId: Id<"knowledge">, groupId?: Id<"groups"> }) ``` --- ## Effect.ts Services ### AgentService ```typescript import { AgentService, runWithServices } from "./services/layers"; const program = Effect.gen(function* () { const agent = yield* AgentService; // Create thread const { threadId } = yield* agent.createThread("user123", { topic: "support" }); // Send message const { response, tokensUsed } = yield* agent.sendMessage(threadId, "Hello!"); // Stream response const stream = yield* agent.streamResponse(threadId, "Tell me more"); // List threads const threads = yield* agent.listThreads("user123"); return { threadId, response, tokensUsed }; }); // Run with services const result = await runWithServices(program); ``` ### RAGService ```typescript import { RAGService, runWithServices } from "./services/layers"; const program = Effect.gen(function* () { const rag = yield* RAGService; // Add document const { documentId } = yield* rag.addDocument( "group:acme", "This is searchable content", { title: "Blog Post", author: "John" } ); // Search const results = yield* rag.search("group:acme", "searchable", 10, 0.6); // Delete document yield* rag.deleteDocument("group:acme", documentId); return results; }); const results = await runWithServices(program); ``` ### RateLimiterService ```typescript import { RateLimiterService, runWithServices } from "./services/layers"; const program = Effect.gen(function* () { const limiter = yield* RateLimiterService; // Check limit (100 requests per minute) const { allowed, remaining } = yield* limiter.checkLimit( "user:123", 100, 60000 ); if (!allowed) { throw new Error("Rate limit exceeded"); } // Get usage const usage = yield* limiter.getUsage("user:123"); // Reset limit (admin operation) yield* limiter.resetLimit("user:123"); return { allowed, remaining, usage }; }); const result = await runWithServices(program); ``` ### WorkflowService ```typescript import { WorkflowService, runWithServices } from "./services/layers"; const program = Effect.gen(function* () { const workflow = yield* WorkflowService; // Start workflow const { workflowId } = yield* workflow.startWorkflow("processOrder", { orderId: "order_123", items: [...] }); // Check status const { status, result } = yield* workflow.getWorkflowStatus(workflowId); // Cancel if needed if (status === "running") { yield* workflow.cancelWorkflow(workflowId); } return { workflowId, status, result }; }); const result = await runWithServices(program); ``` --- ## Common Patterns ### Authentication ```typescript // In mutations const identity = await ctx.auth.getUserIdentity(); if (!identity) { throw new Error("Unauthenticated"); } // Find user entity const user = await ctx.db .query("entities") .withIndex("group_type", q => q.eq("groupId", groupId).eq("type", "user")) .filter(q => q.eq(q.field("properties.userId"), identity.tokenIdentifier)) .first(); ``` ### Multi-Tenant Isolation ```typescript // ALWAYS filter by groupId const entities = await ctx.db .query("entities") .withIndex("by_group", q => q.eq("groupId", groupId)) .collect(); ``` ### Event Logging ```typescript // After any mutation await ctx.db.insert("events", { groupId, type: "thing_created", // or thing_updated, thing_deleted actorId: user._id, targetId: entityId, timestamp: Date.now(), metadata: { entityType: "blog_post", entityName: "My First Post", action: "created" } }); ``` ### Error Handling with Effect.ts ```typescript import { Effect } from "effect"; import { ValidationError, DatabaseError } from "./services/layers"; const program = Effect.gen(function* () { // Validate input if (!name || name.trim().length < 2) { return yield* Effect.fail(new ValidationError({ field: "name", message: "Name must be at least 2 characters" })); } // Wrap database operation const result = yield* Effect.tryPromise({ try: () => ctx.db.insert("entities", { ... }), catch: (cause) => new DatabaseError({ cause, operation: "mutation", table: "entities" }) }); return result; }); // Handle errors const result = await Effect.match(program, { onFailure: (error) => { if (error._tag === "ValidationError") { console.error(`Validation failed: ${error.message}`); } else if (error._tag === "DatabaseError") { console.error(`Database error: ${error.operation}`); } }, onSuccess: (value) => { console.log("Success:", value); } }); ``` --- ## Connection Types (25 total) ```typescript // Ownership (2) "owns" | "created_by" // AI Relationships (3) "clone_of" | "trained_on" | "powers" // Content Relationships (5) "authored" | "generated_by" | "published_to" | "part_of" | "references" // Community Relationships (4) "member_of" | "following" | "moderates" | "participated_in" // Business Relationships (3) "manages" | "reports_to" | "collaborates_with" // Token Relationships (3) "holds_tokens" | "staked_in" | "earned_from" // Product Relationships (4) "purchased" | "enrolled_in" | "completed" | "teaching" // Consolidated Types (7) "transacted" | "notified" | "referred" | "communicated" | "delegated" | "approved" | "fulfilled" ``` --- ## Event Types (67 total, subset shown) ```typescript // Thing Lifecycle "thing_created" | "thing_updated" | "thing_deleted" // User Events "user_registered" | "user_verified" | "user_login" | "profile_updated" // Authentication Events "password_reset_requested" | "email_verified" | "two_factor_enabled" // Group Events "user_invited_to_group" | "user_joined_group" | "user_removed_from_group" // Agent Events "agent_executed" | "agent_completed" | "agent_failed" // Token Events "token_minted" | "tokens_purchased" | "tokens_transferred" // Course Events "course_enrolled" | "lesson_completed" | "certificate_earned" // Analytics Events "metric_calculated" | "insight_generated" | "prediction_made" // Consolidated Events "content_event" | "payment_event" | "commerce_event" | "communication_event" ``` --- ## Type Safety All types are auto-generated from the schema. Use the generated types: ```typescript import { Id, Doc } from "./_generated/dataModel"; // Type-safe IDs const groupId: Id<"groups"> = "..."; const entityId: Id<"entities"> = "..."; // Type-safe documents const group: Doc<"groups"> = await ctx.db.get(groupId); const entity: Doc<"entities"> = await ctx.db.get(entityId); ``` --- ## Performance Tips 1. **Use indexes:** Always use indexed fields for queries 2. **Paginate:** Use limit/offset for large result sets 3. **Filter efficiently:** Filter by indexed fields first 4. **Avoid full scans:** Use .withIndex() instead of .filter() when possible 5. **Batch operations:** Use bulkCreate for multiple connections --- ## Security Checklist - ✅ Authenticate user (ctx.auth.getUserIdentity()) - ✅ Validate group access - ✅ Filter by groupId (multi-tenant isolation) - ✅ Check entity belongs to group - ✅ Log all mutations (audit trail) - ✅ Validate inputs - ✅ Handle errors gracefully --- **Status:** Production-Ready **Last Updated:** 2025-10-24 **Version:** 1.0.0