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 }>;
ExportRole
defineTypeBuild a schema handle from Zod fields + options
relDeclare outgoing relations (one / many, thunk for cycles)
InferTypePlain entity row type
InferResolvedTypeRow type after resolve expansion
rollup / formulaComputed 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);
});
FunctionBehavior
liveEntitiesLive list for a type; optional where, resolve
liveEntityLive single row by id; read(id) first paint, then id-scoped sub
liveQueryRaw 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:

LayerPackagePersisted?
Persisted graph + live readstrellis/schema, trellis/client, trellis/serverYes
Ephemeral meshtrellis/realtimeNo
Peer synctrellis/syncYes (op log)

Reference implementation: examples/graph-nav in the trellis repo (React, Vue, Svelte parity).