Skip to Content
API ReferenceEntities API

Entities API

The Entities API provides methods for creating, updating, querying, and deleting entities in your Synap data pod.

Overview

All mutations are event-sourced - they create immutable event records that are processed asynchronously.
All queries are direct reads - they query the database directly for fast performance.

import { SynapSDK } from '@synap/sdk' const synap = new SynapSDK({ url: 'https://api.synap.app', apiKey: 'your-api-key' }) // Access the Entities API synap.entities

Methods

create()

Create a new entity (event-sourced).

Signature:

create<T extends EntityType>(input: CreateEntityInput<T>): Promise<{ entityId: string }>

Parameters:

interface CreateEntityInput<T extends EntityType> { type: T // Entity type title: string // Display title preview?: string // Short description content?: string // Full content metadata?: Record<string, unknown> // Type-specific data }

Entity Types:

  • 'task' - Tasks and todos
  • 'note' - Notes and documents
  • 'person' - People and contacts
  • 'event' - Calendar events
  • 'file' - File references

Example:

// Create a task const { entityId } = await synap.entities.create({ type: 'task', title: 'Review PR #123', preview: 'Code review for authentication feature', metadata: { priority: 'high', dueDate: '2024-12-25', status: 'todo', estimatedHours: 2 } }) console.log('Created task:', entityId)

Type-Specific Metadata:

// Task metadata { priority?: 'low' | 'medium' | 'high' status?: 'todo' | 'in_progress' | 'done' dueDate?: string completedAt?: string } // Note metadata { tags?: string[] category?: string isPublic?: boolean } // Person metadata { email?: string phone?: string company?: string role?: string } // Event metadata { startDate: string endDate?: string location?: string attendees?: string[] }

What Happens:

  1. SDK generates unique ID
  2. Publishes entities.create.requested event
  3. Inngest worker processes event
  4. Entity inserted into database
  5. entities.create.validated event emitted

update()

Update an existing entity (event-sourced).

Signature:

update(id: string, input: UpdateEntityInput): Promise<{ success: boolean }>

Parameters:

interface UpdateEntityInput { title?: string preview?: string content?: string metadata?: Record<string, unknown> }

Example:

// Update task status await synap.entities.update(taskId, { metadata: { status: 'done', completedAt: new Date().toISOString() } }) // Update note content await synap.entities.update(noteId, { title: 'Updated Meeting Notes', content: '# New content...' })

Partial Updates:

// Only update specific fields await synap.entities.update(entityId, { metadata: { priority: 'low' // Other metadata fields remain unchanged } })

delete()

Soft delete an entity (event-sourced).

Signature:

delete(id: string): Promise<{ success: boolean }>

Example:

await synap.entities.delete(entityId)

Note: This is a soft delete - the entity is marked as deleted but remains in the database for audit purposes.


get()

Get a single entity by ID (direct read).

Signature:

get(id: string): Promise<Entity>

Returns:

interface Entity { id: string type: EntityType title: string preview?: string content?: string metadata: Record<string, unknown> userId: string createdAt: Date updatedAt: Date deletedAt?: Date }

Example:

const task = await synap.entities.get(taskId) console.log(task.title) // "Review PR #123" console.log(task.metadata) // { priority: 'high', ... } console.log(task.createdAt) // 2024-12-20T10:30:00Z

Error Handling:

try { const entity = await synap.entities.get(entityId) } catch (error) { if (error.code === 'NOT_FOUND') { console.log('Entity does not exist') } }

list()

Query entities with filters (direct read).

Signature:

list(options?: ListEntitiesOptions): Promise<Entity[]>

Parameters:

interface ListEntitiesOptions { type?: EntityType // Filter by type limit?: number // Max results (default: 50, max: 100) }

Example:

// Get all tasks const tasks = await synap.entities.list({ type: 'task', limit: 20 }) // Get all entities const allEntities = await synap.entities.list() // Get recent notes const recentNotes = await synap.entities.list({ type: 'note', limit: 10 })

Pagination:

// Get first page const page1 = await synap.entities.list({ limit: 50 }) // For next pages, filter by createdAt const page2 = await synap.entities.list({ limit: 50 // TODO: Add cursor-based pagination })

Full-text search across entities (direct read).

Signature:

search(options: SearchOptions): Promise<Entity[]>

Parameters:

interface SearchOptions { query: string // Search query types?: EntityType[] // Filter by types limit?: number // Max results (default: 10) }

Example:

// Search all entities const results = await synap.entities.search({ query: 'project planning' }) // Search specific types const taskResults = await synap.entities.search({ query: 'urgent', types: ['task'], limit: 20 }) // Search notes and tasks const mixed = await synap.entities.search({ query: 'meeting', types: ['note', 'task'] })

Search Behavior:

  • Searches in: title, preview, content
  • Case-insensitive
  • Partial matching
  • Results ordered by relevance

Common Patterns

Create with Relationships

// Create task and person const { entityId: taskId } = await synap.entities.create({ type: 'task', title: 'Design review' }) const { entityId: personId } = await synap.entities.create({ type: 'person', title: 'Marie Johnson', metadata: { email: 'marie@example.com' } }) // Link them await synap.relations.create(taskId, personId, 'assigned_to')

Batch Operations

// Create multiple entities const taskIds = await Promise.all([ synap.entities.create({ type: 'task', title: 'Task 1' }), synap.entities.create({ type: 'task', title: 'Task 2' }), synap.entities.create({ type: 'task', title: 'Task 3' }) ]) console.log('Created', taskIds.length, 'tasks')

Update with Optimistic UI

// Update UI immediately setTask({ ...task, metadata: { ...task.metadata, status: 'done' } }) // Persist change await synap.entities.update(task.id, { metadata: { status: 'done' } })

Filter and Transform

// Get all tasks and filter const tasks = await synap.entities.list({ type: 'task' }) const highPriority = tasks.filter(t => t.metadata.priority === 'high' ) const overdue = tasks.filter(t => new Date(t.metadata.dueDate) < new Date() )

Best Practices

✅ Do’s

// Use descriptive titles await synap.entities.create({ type: 'task', title: 'Review authentication PR #123' // Good }) // Store structured metadata await synap.entities.create({ type: 'task', title: 'Call client', metadata: { priority: 'high', dueDate: '2024-12-25', client: 'Acme Corp' } }) // Check existence before operations const entity = await synap.entities.get(id) if (!entity.deletedAt) { await synap.entities.update(id, { ... }) }

❌ Don’ts

// Don't store sensitive data in title/preview await synap.entities.create({ title: 'Password: abc123' // Bad! }) // Don't exceed reasonable sizes await synap.entities.create({ content: hugeMBDocument // Use file storage instead }) // Don't create without proper error handling await synap.entities.create({ ... }) // Add try/catch!

Type Safety

The SDK is fully typed with TypeScript:

import { Entity, EntityType } from '@synap/sdk' // Type-safe entity creation const task = await synap.entities.create({ type: 'task', // Autocomplete works! title: 'My task', metadata: { priority: 'high', // Type-checked based on entity type // TypeScript will suggest valid fields } }) // Type-safe queries const tasks: Entity<'task'>[] = await synap.entities.list({ type: 'task' })

Next Steps

Last updated on