Schema (typed SDK)
defineType, live queries, relation resolve, and schema-typed mutations on the Trellis graph.
The trellis/schema package is the typed SDK surface for persisted graph entities: one defineType call is runtime ontology, TypeScript types, EQL query builders, and mutation helpers.
Use it when you want Jazz-style DX (typed rows, live lists, relation expand) on Trellis's semantic EAV graph, not a separate document store.
Install
npm install trellis@^3.2.0
Peer: zod@^3 for field validators on defineType.
defineType
Declare an entity type once. The return value is a schema handle you pass to registerType, liveEntities, framework hooks, and entityMutations.
import { defineType, rel } from "trellis/schema";
import { z } from "zod";
import type { InferResolvedType, InferType } from "trellis/schema";
export const NavItem = defineType(
"NavItem",
{
label: z.string(),
href: z.string().optional(),
order: z.number(),
},
{
title: "label",
relations: { section: rel("NavSection") },
}
);
export const NavSection = defineType(
"NavSection",
{
label: z.string(),
order: z.number(),
collapsed: z.boolean(),
},
{
title: "label",
relations: { items: rel(() => NavItem, "many") },
}
);
export type NavSectionT = InferType<typeof NavSection>;
export type NavSectionLoaded = InferResolvedType<typeof NavSection, { items: true }>;
| Export | Role |
|---|---|
defineType | Build a schema handle from Zod fields + options |
rel | Declare outgoing relations (one / many, thunk for cycles) |
InferType | Plain entity row type |
InferResolvedType | Row type after resolve expansion |
rollup / formula | Computed fields on the schema (advanced) |
Register with the kernel
Before reads or writes, register schemas with the connected client. Re-registration is idempotent (409 swallowed in remote mode, no-op if ontology already exists).
import { TrellisDb } from "trellis/client/sdk";
const client = new TrellisDb({ url: "http://localhost:8230" });
await client.registerType(NavItem);
await client.registerType(NavSection);
EQL builders
entitiesQuery and entityQuery compile type-safe EQL-S for subscriptions and one-off reads.
import { entitiesQuery, entityQuery, whereCondition } from "trellis/schema";
// All NavSection rows
entitiesQuery("NavSection");
// Filtered list (operators: eq default, ne, gt, gte, lt, lte, in)
entitiesQuery("NavItem", { section: "nav-section:abc" });
entitiesQuery("NavItem", whereCondition("order", "gte", 1));
// Single-entity subscription (id-scoped, not a full-type scan)
entityQuery("NavItem", "nav-item:xyz");
Live reads (trellis/client)
Framework-agnostic primitives live in trellis/client. They require remote mode (WebSocket subscriptions against trellis serve or compatible server).
import { liveEntities, liveEntity } from "trellis/client";
const sections = liveEntities(client, NavSection, {
resolve: { items: true },
});
sections.start();
sections.signal.subscribe(({ data, loading, error }) => {
console.log(loading, error, data);
});
| Function | Behavior |
|---|---|
liveEntities | Live list for a type; optional where, resolve |
liveEntity | Live single row by id; read(id) first paint, then id-scoped sub |
liveQuery | Raw EQL string subscription (escape hatch) |
Server hydration (3.2.0): when the client passes resolve, the server expands relations on the wire before push, so the UI avoids N+1 reads.
See Client SDK and Typed SDK guide.
Typed mutations
import { entityMutations } from "trellis/schema";
const nav = entityMutations(client, NavSection);
await nav.create({ label: "Docs", order: 0, collapsed: false });
await nav.update("nav-section:abc", { collapsed: true });
Framework adapters wrap the same helpers: useMutation in trellis/vue/typed, trellis/react/typed, trellis/svelte/typed.
Three collaboration layers
Do not confuse schema live reads with ephemeral multiplayer or VCS sync:
| Layer | Package | Persisted? |
|---|---|---|
| Persisted graph + live reads | trellis/schema, trellis/client, trellis/server | Yes |
| Ephemeral mesh | trellis/realtime | No |
| Peer sync | trellis/sync | Yes (op log) |
Reference implementation: examples/graph-nav in the trellis repo (React, Vue, Svelte parity).