Go from an empty directory to a live, syncing document store in about five minutes. This guide installs the CLI, provisions a local store, and walks the first read-write round-trip.
Install the CLI
The halyard command-line tool scaffolds a project, runs a local store for development, and deploys to the edge. Install it globally with your package manager of choice.
Node 20 or newer. The CLI ships as a single static binary but the client library targets modern runtimes. Older Node releases are not supported.
Initialize a store
A store is created once and shared across your app. Call createStore at module scope so a single connection is reused for every read and write.
import { createStore } from "@halyard/client";
export const store = createStore({
url: process.env.HALYARD_URL,
token: process.env.HALYARD_TOKEN,
});Configuration
Both fields fall back to their environment variables, so you can omit them entirely in a configured environment. During local development the CLI writes a .env.local for you when you run halyard init.
url— the store endpoint, or a local socket in development.token— a scoped access token; never embed a root key in client code.cache— optional eviction policy for the on-device mirror.
Your first write
Documents live inside named collections. Writing one is a single call — the change is committed locally first, then fans out to every connected client.
import { store } from "./store";
await store.collection("notes").put({
id: "welcome",
title: "Welcome to Halyard",
body: "Edits sync to every client in real time.",
});Writes are optimistic. The promise resolves the moment the change is durable on-device. Sync happens in the background, so your UI never blocks on the network.
Reading data back
Read a whole collection with query. Filters, ordering, and limits run against the local mirror, so results return instantly even offline.
const notes = await store.collection("notes").query({
where: { archived: false },
orderBy: "-updatedAt",
limit: 20,
});Live queries
Swap query for live and the same shape returns a subscription that re-emits whenever a matching document changes — anywhere in the fleet.
const unsubscribe = store
.collection("notes")
.live({ where: { archived: false } })
.subscribe((notes) => render(notes));Always unsubscribe. A live query holds an open channel. Call the returned function when the view unmounts, or long-lived clients will accumulate idle subscriptions.
Next steps
You have a store, a write, and a live read. From here the guides go deeper into the pieces that make a production app.
- Conflict resolution — how concurrent edits merge, and how to override the default.
- Offline mode — queueing, back-pressure, and reconnection.
- Authentication — minting scoped tokens and rotating keys.
© Halyard contributors. Docs licensed CC BY 4.0.