Typed SDK (live graph reads)

defineType to registerType to liveEntities typed, hydrated, real-time reads on persisted graph data.

Trellis 3.2 adds a typed SDK for building live UIs on the persisted semantic graph: one schema definition drives ontology registration, TypeScript types, EQL subscriptions, relation resolve, and framework hooks.

This is distinct from Realtime (ephemeral) (presence, chat, cursors, not in the op log) and from Collaboration & Sync (peer op replication).

When to use it

NeedUse
Live sidebar, CMS collections, nav trees on graph entitiesTyped SDK
Cursors, typing indicators, disposable chattrellis/realtime
Audit trail, merge, offline peer synctrellis/sync + VCS

Architecture

Rendering Chart
  1. defineType: Zod fields + relations → schema handle (Schema API).
  2. registerType: POST ontology to server (idempotent).
  3. liveEntities / liveEntity subscribe with server-side hydration + optional resolve.
  4. Framework hooktrellis/vue/typed (or React / Svelte) binds signals to your UI.

Quickstart

1. Schema

// schema.ts
import { defineType, rel } from "trellis/schema";
import { z } from "zod";

export const Task = defineType(
  "Task",
  { title: z.string(), done: z.boolean() },
  { title: "title" }
);

2. Server + client

npm install trellis@^3.2.0
npx trellis init
npx trellis serve --port 8230
import { TrellisDb } from "trellis/client/sdk";

const client = new TrellisDb({ url: "http://localhost:8230" });

await client.registerType(Task);

3. Live list (framework-agnostic)

import { liveEntities } from "trellis/client";

const tasks = liveEntities(client, Task, { where: { done: false } });
tasks.start();

4. Vue component

import { useEntities, useMutation } from "trellis/vue/typed";

const { data, loading } = useEntities(client, Task);
const task = useMutation(client, Task);

See Typed framework bindings for React and Svelte.

Relation resolve

Load a parent type and expand children in one subscription pass:

useEntities(client, NavSection, { resolve: { items: true } });

The server batches child loads grouped by foreign key. Client-side N+1 is avoided when the server marks the push as resolved.

Filters

List queries accept field equality or explicit operators:

import { whereCondition } from "trellis/schema";

liveEntities(client, Task, {
  where: whereCondition("order", "gte", 1),
});

Supported ops: eq (default), ne, gt, gte, lt, lte, in.

Single entity by id

liveEntity uses read(id) for first paint, then an id-scoped subscription (entityQuery), not a full-type scan.

import { useEntity } from "trellis/vue/typed";

const { data: task } = useEntity(client, Task, taskId);

Try the parity demo

The graph-nav example (trellis repo) exercises the full stack across three frameworks:

git clone https://github.com/trentbrew/trellis.git
cd trellis
just graph-nav
URLFramework
http://localhost:4200/react/React + trellis/react/typed
http://localhost:4200/vue/Vue + trellis/vue/typed
http://localhost:4200/svelte/Svelte + trellis/svelte/typed

Open the same URL in two browser tabs; add a nav section in one tab and watch the other update.

Hosting notes

SurfaceRequirement
trellis.computer docs embedsStatic demos only (no live typed SDK on Vercel static hosting)
Your appLong-lived trellis serve or Sprites deploy; WebSocket /realtime
Cross-browser ephemeral UIOptional relay (trellis/realtime), not required for typed reads

Studio and Trellis Cloud sandboxes run the full stack inside E2B; see Cloud hosting.

API reference