This guide walks through adding a new collection to your app: declaring its schema, querying it from the client, and persisting writes. By the end, you’ll have a working CRUD page backed by a Durable Object that syncs in real time across every connected client. For background on envelopes, scopes, and the optimistic pipeline, see Data model and Real-time sync.Documentation Index
Fetch the complete documentation index at: https://docs.deep.space/llms.txt
Use this file to discover all available pages before exploring further.
Define the schema
Schemas live undersrc/schemas/, one file per collection. Create a new collection called notes:
src/schemas.ts:
npx deepspace dev if it’s running - schemas are picked up at worker startup.
Schemas are baked into your worker at deploy time. There is no runtime schema registry; to add or change a schema in production, redeploy.
Read records
useQuery subscribes to a collection and streams updates over a WebSocket. Each record arrives as an envelope, with your fields under .data:
useQuery options. The Durable Object applies where server-side before broadcasting, so unauthorized records never leave the worker.
Write records
useMutations returns create / put / remove plus *Confirmed variants. Each mutation applies optimistically - the local store updates before the server confirms.
createreturns therecordIdimmediately. The ID is generated client-side (timestamp + random suffix) before the write is sent, so you can navigate to/notes/${id}without waiting for the server.putis merge semantics. The server applies{ ...existing, ...patch }. Send only the fields you’re changing - don’t spread the whole row.removeis a hard delete. There’s no soft-delete primitive at the records layer.- Use
createConfirmedwhen persistence matters before the next step. Plaincreateresolves on local update; if the server later rejects (RBAC, validation), the optimistic write rolls back.createConfirmedwaits for the Durable Object ack and rejects the promise on server denial.
A complete CRUD example
JSON columns
For structured field data, useinterpretation: { kind: 'json' }:
JSON.stringify / JSON.parse on either end.
Per-user privacy
Permissions are enforced server-side. To make notes private per user:'own' resolves against record.createdBy by default; override with ownerField if a different column determines ownership. For published/draft visibility, collaborators, and team rules, see Permissions.
Performance tips
- Filter at the query.
whereruns server-side, so unauthorized or unwanted rows never cross the wire. - Use
limiton long lists. Initial subscription sends every matching record. For thousands of rows, slice by date or category. (Cursor pagination is on the roadmap.) - Lift
useQueryto a parent. Identical queries deduplicate to one WebSocket subscription, but each mount still re-renders on every store change - fetch once at the top of the page and pass records down via props. - Patch, don’t replace.
putwith a partial patch keeps the wire payload small and avoids overwriting concurrent edits.
The SDK lints each schema at boot and prints findings prefixed
[schema-lint] to the worker console. See Schema-lint warnings for the three shapes and their fixes.Next steps
- Permissions - role-based access control on collections.
- Server actions - privileged writes that bypass user RBAC.
- Records reference - full hooks API surface.
- Schema reference - column types and permission rules.