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.

888 lines (691 loc) 18.5 kB
--- title: Integration Guide dimension: connections category: integration-guide.md tags: ontology related_dimensions: events, groups, knowledge, people, things scope: global created: 2025-11-03 updated: 2025-11-03 version: 1.0.0 ai_context: | This document is part of the connections dimension in the integration-guide.md category. Location: one/connections/integration-guide.md Purpose: Documents one platform integration guide Related dimensions: events, groups, knowledge, people, things For AI agents: Read this to understand integration guide. --- # ONE Platform Integration Guide **Version:** 1.0.0 **Created:** 2025-01-25 **Purpose:** How to integrate external applications with ONE Platform --- ## Overview ONE Platform provides a complete API for external integrations. This guide shows you how to: 1. **Authenticate** your application 2. **Connect** to the 6-dimension ontology 3. **Build** multi-tenant features 4. **Handle** rate limiting and errors 5. **Deploy** production-ready integrations --- ## Quick Start (5 Minutes) ### Step 1: Install Dependencies ```bash npm install convex @better-auth/client ``` ### Step 2: Initialize Convex Client ```typescript // lib/convex.ts import { ConvexHttpClient } from "convex/browser"; export const convex = new ConvexHttpClient( process.env.NEXT_PUBLIC_CONVEX_URL! ); ``` ### Step 3: Authenticate ```typescript // lib/auth.ts import { createAuthClient } from "@better-auth/client"; export const auth = createAuthClient({ baseURL: process.env.NEXT_PUBLIC_AUTH_URL! }); // Sign in await auth.signIn.email({ email: "user@example.com", password: "password123" }); ``` ### Step 4: Query Data ```typescript import { convex } from "./lib/convex"; import { api } from "@/convex/_generated/api"; // Get your group const group = await convex.query(api.queries.groups.getBySlug, { slug: "my-org" }); // List entities const entities = await convex.query(api.queries.entities.list, { groupId: group._id, type: "course", status: "published" }); ``` ### Step 5: Create Data ```typescript // Create new entity const courseId = await convex.mutation(api.mutations.entities.create, { groupId: group._id, type: "course", name: "TypeScript Fundamentals", properties: { description: "Learn TypeScript from scratch", price: 99, duration: 40 }, status: "published" }); ``` **Done!** You've integrated with ONE Platform in 5 minutes. --- ## Authentication Methods ### 1. Email/Password ```typescript import { auth } from "./lib/auth"; // Sign up await auth.signUp.email({ email: "user@example.com", password: "secure_password", name: "John Doe" }); // Sign in await auth.signIn.email({ email: "user@example.com", password: "secure_password" }); // Sign out await auth.signOut(); ``` --- ### 2. OAuth (GitHub, Google) ```typescript // Redirect to OAuth provider await auth.signIn.social({ provider: "github", callbackURL: "/auth/callback" }); // Handle callback await auth.handleCallback(); ``` **Supported Providers:** - GitHub - Google - More providers coming soon --- ### 3. Magic Links ```typescript // Request magic link await auth.signIn.magicLink({ email: "user@example.com" }); // User clicks link in email, automatically signs in ``` **Use Case:** Passwordless authentication, better UX --- ### 4. API Keys (Server-Side) ```typescript // Generate API key (one-time, save securely) const apiKey = await convex.mutation(api.mutations.apiKeys.create, { groupId, name: "Production API Key", permissions: ["read", "write"] }); // Use API key for authentication const convex = new ConvexHttpClient( process.env.CONVEX_URL, { headers: { "Authorization": `Bearer ${process.env.API_KEY}` } } ); ``` **Use Case:** Server-to-server integrations, CI/CD pipelines --- ## Multi-Tenancy Best Practices ### Rule 1: Always Scope to GroupId ```typescript // CORRECT - Scoped to group const entities = await convex.query(api.queries.entities.list, { groupId: currentGroup._id, type: "course" }); // WRONG - Missing groupId (security risk!) const entities = await convex.query(api.queries.entities.listAll); ``` **Why:** GroupId provides perfect data isolation between organizations. --- ### Rule 2: Validate Group Access ```typescript // CORRECT - Validate entity belongs to expected group const entity = await convex.query(api.queries.entities.getById, { entityId: courseId, groupId: currentGroup._id // Security check }); if (!entity) { throw new Error("Entity not found in your organization"); } ``` **Why:** Prevents cross-tenant data leakage. --- ### Rule 3: Use Hierarchical Groups ```typescript // Create organization (top-level) const orgId = await convex.mutation(api.mutations.groups.create, { slug: "acme-corp", name: "Acme Corporation", type: "organization", settings: { visibility: "private", joinPolicy: "invite_only", plan: "enterprise" } }); // Create department (nested) const engineeringId = await convex.mutation(api.mutations.groups.create, { slug: "engineering", name: "Engineering", type: "community", parentGroupId: orgId // Nested under org }); // Create team (nested under department) const backendId = await convex.mutation(api.mutations.groups.create, { slug: "backend", name: "Backend Team", type: "community", parentGroupId: engineeringId // Nested under engineering }); // Query all entities in org + subgroups const allEntities = await convex.query( api.queries.groups.getEntitiesInHierarchy, { rootGroupId: orgId, entityType: "project" } ); ``` **Use Case:** Organizations Departments Teams Projects --- ## Working with the 6-Dimension Ontology ### Dimension 1: Groups **What:** Multi-tenant isolation boundary with hierarchical nesting **Types:** - `friend_circle` - Personal friend groups - `business` - Small businesses - `community` - Communities - `dao` - Decentralized organizations - `government` - Government agencies - `organization` - Enterprises **Example:** ```typescript const group = await convex.mutation(api.mutations.groups.create, { slug: "my-org", name: "My Organization", type: "business", settings: { visibility: "private", joinPolicy: "invite_only", plan: "pro" } }); ``` --- ### Dimension 3: Things (Entities) **What:** All nouns in the system **Example Types:** - `user` - Platform users - `course` - Educational courses - `lesson` - Course lessons - `project` - Portfolio projects - `blog_post` - Blog articles - `token` - Digital tokens - `agent` - AI agents **Example:** ```typescript const courseId = await convex.mutation(api.mutations.entities.create, { groupId, type: "course", name: "TypeScript Fundamentals", properties: { description: "Learn TypeScript", price: 99, duration: 40, instructor: "Jane Doe" }, status: "published" }); ``` --- ### Dimension 4: Connections **What:** Relationships between entities **Example Types:** - `owns` - Ownership - `enrolled_in` - Course enrollment - `created` - Authorship - `tagged_with` - Categorization - `member_of` - Group membership **Example:** ```typescript // Student enrolls in course const connectionId = await convex.mutation(api.mutations.connections.create, { groupId, fromEntityId: studentId, toEntityId: courseId, relationshipType: "enrolled_in", metadata: { enrolledAt: Date.now(), progress: 0 } }); // Query enrolled courses const enrollments = await convex.query(api.queries.connections.list, { groupId, fromEntityId: studentId, relationshipType: "enrolled_in" }); ``` --- ### Dimension 5: Events **What:** Audit trail of all actions **Example Types:** - `thing_created` - Entity created - `thing_updated` - Entity updated - `thing_deleted` - Entity deleted - `user_logged_in` - User logged in - `payment_processed` - Payment completed **Example:** ```typescript // Events are automatically logged by mutations await convex.mutation(api.mutations.entities.create, { groupId, type: "course", name: "TypeScript Fundamentals" }); // Automatically creates "thing_created" event // Query event timeline const timeline = await convex.query(api.queries.events.list, { groupId, limit: 20 }); ``` **Use Case:** Complete audit trail, activity feeds, compliance --- ### Dimension 6: Knowledge **What:** RAG (Retrieval-Augmented Generation) for AI features **Example:** ```typescript // Add knowledge const knowledgeId = await convex.mutation(api.mutations.knowledge.add, { groupId, text: "TypeScript is a typed superset of JavaScript", labels: ["typescript", "programming", "education"], sourceThingId: courseId }); // Semantic search const results = await convex.query(api.queries.knowledge.search, { groupId, query: "What is TypeScript?", limit: 5 }); ``` **Use Case:** AI chatbots, semantic search, recommendations --- ## Real-Time Subscriptions ONE Platform provides real-time updates via Convex subscriptions. ### React Example ```typescript import { useQuery, useMutation } from "convex/react"; import { api } from "@/convex/_generated/api"; export function CourseList({ groupId }) { // Real-time subscription - updates automatically const courses = useQuery(api.queries.entities.list, { groupId, type: "course", status: "published" }); const createCourse = useMutation(api.mutations.entities.create); if (courses === undefined) { return <div>Loading...</div>; } return ( <div> {courses.map(course => ( <div key={course._id}>{course.name}</div> ))} <button onClick={() => createCourse({ groupId, type: "course", name: "New Course" })}> Create Course </button> </div> ); } ``` **Behavior:** - `courses` updates automatically when data changes - No polling needed - Sub-100ms latency - Works globally via Convex edge network --- ### Vanilla JavaScript Example ```typescript import { ConvexHttpClient } from "convex/browser"; import { api } from "@/convex/_generated/api"; const convex = new ConvexHttpClient(process.env.CONVEX_URL); // Subscribe to query const unsubscribe = convex.subscribe( api.queries.entities.list, { groupId, type: "course" }, (courses) => { console.log("Courses updated:", courses); // Update UI with new data } ); // Later: unsubscribe unsubscribe(); ``` --- ## Error Handling ### Handle Common Errors ```typescript try { const entity = await convex.query(api.queries.entities.getById, { entityId: courseId, groupId }); } catch (error) { // Type-safe error handling if (error.code === "NOT_FOUND") { console.error("Entity not found"); } else if (error.code === "UNAUTHENTICATED") { // Redirect to login window.location.href = "/login"; } else if (error.code === "GROUP_ACCESS_DENIED") { console.error("You don't have access to this group"); } else { // Log unexpected errors console.error("Unexpected error:", error); } } ``` --- ### Retry with Exponential Backoff ```typescript async function retryQuery(fn, maxRetries = 3) { let lastError; for (let i = 0; i < maxRetries; i++) { try { return await fn(); } catch (error) { lastError = error; const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s await new Promise(resolve => setTimeout(resolve, delay)); } } throw lastError; } // Usage const courses = await retryQuery(() => convex.query(api.queries.entities.list, { groupId, type: "course" }) ); ``` --- ## Rate Limiting ### Rate Limit Headers ```typescript const response = await fetch("/api/entities", { headers: { "Authorization": `Bearer ${apiKey}` } }); console.log(response.headers.get("X-RateLimit-Limit")); // 1000 console.log(response.headers.get("X-RateLimit-Remaining")); // 950 console.log(response.headers.get("X-RateLimit-Reset")); // 1622547600 ``` ### Handle Rate Limit Errors ```typescript try { await convex.mutation(api.mutations.entities.create, { /* ... */ }); } catch (error) { if (error.code === "RATE_LIMIT_EXCEEDED") { const resetTime = parseInt(error.headers["X-RateLimit-Reset"]); const waitSeconds = Math.ceil((resetTime - Date.now()) / 1000); console.log(`Rate limit exceeded. Retry in ${waitSeconds} seconds`); } } ``` --- ## Pagination ### Cursor-Based Pagination ```typescript async function* paginateEntities(groupId, type) { let cursor = null; while (true) { const response = await convex.query(api.queries.entities.list, { groupId, type, limit: 100, cursor }); yield response.items; if (!response.hasMore) break; cursor = response.nextCursor; } } // Usage for await (const batch of paginateEntities(groupId, "course")) { console.log("Batch:", batch); } ``` --- ## Webhooks (Coming Soon) Receive real-time notifications when data changes. ### Subscribe to Webhook ```typescript const webhook = await convex.mutation(api.mutations.webhooks.create, { groupId, url: "https://yourapp.com/webhooks/one", events: ["thing_created", "thing_updated"], secret: "your_webhook_secret" }); ``` ### Handle Webhook ```typescript // Your webhook endpoint app.post("/webhooks/one", async (req, res) => { // Verify signature const signature = req.headers["x-one-signature"]; const payload = JSON.stringify(req.body); const expected = crypto .createHmac("sha256", process.env.WEBHOOK_SECRET) .update(payload) .digest("hex"); if (signature !== expected) { return res.status(401).send("Invalid signature"); } // Process event const { event, data } = req.body; if (event === "thing_created") { console.log("New entity created:", data); } res.status(200).send("OK"); }); ``` --- ## Example Integrations ### 1. Mobile App (React Native) ```typescript import { ConvexHttpClient } from "convex/browser"; import { api } from "@/convex/_generated/api"; const convex = new ConvexHttpClient( process.env.EXPO_PUBLIC_CONVEX_URL ); export function CourseScreen({ groupId }) { const [courses, setCourses] = useState([]); useEffect(() => { const unsubscribe = convex.subscribe( api.queries.entities.list, { groupId, type: "course" }, (data) => setCourses(data) ); return () => unsubscribe(); }, [groupId]); return ( <FlatList data={courses} keyExtractor={item => item._id} renderItem={({ item }) => <CourseCard course={item} />} /> ); } ``` --- ### 2. Zapier Integration ```typescript // Zapier trigger: New Course Created const newCourse = { key: "new_course", noun: "Course", display: { label: "New Course", description: "Triggers when a new course is created" }, operation: { perform: async (z, bundle) => { const courses = await z.request({ url: `${bundle.authData.baseUrl}/api/entities`, params: { groupId: bundle.authData.groupId, type: "course", limit: 100 } }); return courses.json(); } } }; ``` --- ### 3. N8N Workflow ```json { "nodes": [ { "name": "ONE Trigger", "type": "n8n-nodes-base.httpRequest", "parameters": { "url": "={{$env.ONE_API_URL}}/api/entities", "authentication": "genericCredentialType", "method": "GET", "queryParameters": { "groupId": "={{$env.ONE_GROUP_ID}}", "type": "course", "status": "published" } } }, { "name": "Process Courses", "type": "n8n-nodes-base.function", "parameters": { "functionCode": "return items.map(item => ({\n json: {\n courseName: item.json.name,\n price: item.json.properties.price\n }\n}));" } } ] } ``` --- ## Performance Optimization ### 1. Use Indexes ```typescript // FAST - Uses group_type index const courses = await convex.query(api.queries.entities.list, { groupId, type: "course" }); // SLOW - No index, filters in memory const courses = (await convex.query(api.queries.entities.list, { groupId })) .filter(e => e.type === "course"); ``` --- ### 2. Batch Requests ```typescript // FAST - Parallel requests const [courses, projects, users] = await Promise.all([ convex.query(api.queries.entities.list, { groupId, type: "course" }), convex.query(api.queries.entities.list, { groupId, type: "project" }), convex.query(api.queries.entities.list, { groupId, type: "user" }) ]); // SLOW - Sequential requests const courses = await convex.query(api.queries.entities.list, { groupId, type: "course" }); const projects = await convex.query(api.queries.entities.list, { groupId, type: "project" }); const users = await convex.query(api.queries.entities.list, { groupId, type: "user" }); ``` --- ### 3. Cache Aggressively ```typescript // Client-side caching const cache = new Map(); async function getCachedEntity(entityId) { if (cache.has(entityId)) { return cache.get(entityId); } const entity = await convex.query(api.queries.entities.getById, { entityId }); cache.set(entityId, entity); // Invalidate after 5 minutes setTimeout(() => cache.delete(entityId), 5 * 60 * 1000); return entity; } ``` --- ## Security Best Practices ### 1. Never Expose API Keys ```typescript // CORRECT - Server-side only // server.ts const apiKey = process.env.ONE_API_KEY; // WRONG - Exposed to client // client.ts const apiKey = "pk_live_12345"; // Never hardcode! ``` --- ### 2. Validate Input ```typescript // CORRECT - Validate before mutation function validateCourse(data) { if (!data.name || data.name.length < 3) { throw new Error("Course name must be at least 3 characters"); } if (data.properties?.price < 0) { throw new Error("Price cannot be negative"); } } validateCourse(courseData); await convex.mutation(api.mutations.entities.create, courseData); ``` --- ### 3. Use HTTPS Only ```typescript // CORRECT const convex = new ConvexHttpClient("https://your-app.convex.cloud"); // WRONG - Never use HTTP in production const convex = new ConvexHttpClient("http://your-app.convex.cloud"); ``` --- ## Support **Documentation:** https://one.ie/docs **API Reference:** /one/connections/api-reference.md **Community:** https://discord.gg/one-platform **Email:** support@one.ie --- **Built for seamless integration and infinite scale.**