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.entitiesMethods
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:
- SDK generates unique ID
- Publishes
entities.create.requestedevent - Inngest worker processes event
- Entity inserted into database
entities.create.validatedevent 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:00ZError 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
})search()
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
- Relations API → - Link entities together
- Events API → - Access event history
- Examples → - See real-world usage