Skip to main content
@pylonsync/sdk is the foundation every other JS package depends on. It’s two things in one package:
  1. A schema DSL for declaring entities, queries, actions, policies in TypeScript instead of editing pylon.manifest.json by hand.
  2. The codegen runtime that compiles your TS schema into the manifest the Pylon server reads.
There’s no HTTP client in this package — for that, use @pylonsync/react (browser) or @pylonsync/sync (any JS host).

Install

Defining your schema

Create app.ts:
Fields are built with the field helper (field.string(), field.int(), field.id("User"), …) and refined with chained modifiers (.unique(), .optional(), .default(v), .defaultNow(), .owner(), .serverOnly(), .crdt(...)). query / action register a name plus a typed input list; the handler itself lives in a functions/*.ts file (see Actions). Then run codegen to materialize the manifest:
pylon dev does this automatically on file change.

Field types

Modifiers are chained methods, not option objects:
For CRDT-backed fields, use .crdt(annotation):
"text" and "counter" are wired end-to-end; the "list", "movable-list", and "tree" annotations are reserved (wire format locked in, server-side projection still landing). CRDT-backed fields don’t go through normal LWW merge; they sync via the binary CRDT broadcast channel. See Loro for the full picture.

Indexes

The first matching prefix on a multi-column index wins for query planning. Pylon translates these to native SQLite/Postgres indexes.

Search config

This wires the entity into the search plugin. Once enabled, the entity is queryable via POST /api/search/Post.

Relations

Relations enable include joins on queries. On the client, request them through the query’s include map (an object keyed by relation name):

Queries

Named, typed query inputs. Each input entry is { name, type, optional? }, where type is a field-type string ("string", "int", `id(User)`, …). Resolve to /api/query/<name>:
query / action register the name + input contract; the handler lives in a functions/<name>.ts file. Use a query() handler for reads and a mutation() / action() handler for writes and computed results.

Actions

Server-side functions with typed args. Resolve to /api/fn/<name>:
The action’s handler lives in a separate file (functions/completeTodo.ts) and the function runtime wires it up:
See Functions for the full handler API.

Policies

Each rule is a per-operation gate: allowRead, allowInsert, allowUpdate, allowDelete. allowWrite is a shared fallback for the three write ops, and allow is the fallback for all four. Expression syntax in RBAC.

Plugins

definePlugin({ name, hooks }) returns a PluginDefinition — a named set of server-side entity lifecycle hooks:
Available hooks: beforeInsert, afterInsert, beforeUpdate, afterUpdate, beforeDelete, afterDelete. Many capabilities don’t need a plugin at all — they’re declarative: full-text search is an entity’s search: option (above), and auth / scheduled jobs are configured with the auth() / cron() helpers passed to buildManifest.

Manifest output

buildManifest({...}) returns a Manifest object. The codegen step writes it to JSON:
The Pylon server reads only the JSON — your TS source isn’t needed at runtime. This is what lets the same backend serve clients in any language.

Where to next