Skip to main content
Stores are schema-driven key-value stores that sync across all connected clients in real time. Define a schema, and the SDK gives you fully typed CRUD operations with automatic validation and real-time change events. Access stores via client.stores.<storeName>.

Schema Definition

Define your store schemas using defineStores() from @collab-kit/utils and pass to the client constructor:
import { defineStores } from '@collab-kit/utils';

const stores = defineStores({
  tasks: {
    title: { type: 'string', required: true },
    completed: { type: 'boolean', default: false },
    assignee: { type: 'string' },
  },
  settings: {
    theme: { type: 'string', required: true, default: 'light' },
    fontSize: { type: 'number', required: true, default: 14 },
    notifications: { type: 'boolean', default: true },
  },
});

const client = new CollabKitClient({
  serverUrl: 'https://api.collab-kit.com',
  authToken: '<jwt>',
  stores,
});
OptionTypeDescription
type'string' | 'number' | 'boolean'The primitive type of the field
requiredbooleanIf true, the field must be present on set. Defaults to false
defaultstring | number | booleanDefault value applied when the field is missing on set

Methods

Add Entry

set({ key, value }) Create or overwrite an entry. Required fields (without defaults) must be provided:
await client.stores.tasks.set({
  key: 'task-1',
  value: { title: 'Build stores feature', assignee: 'user-001' },
});
// 'completed' defaults to false (from schema)

Get Entry

get({ key }) Fetch a single entry by key. Returns null if the key doesn’t exist:
const task = await client.stores.tasks.get({ key: 'task-1' });
// { title: 'Build stores feature', completed: false, assignee: 'user-001' } | null

Get All Entries

getAll() Fetch all entries in the store:
const all = await client.stores.tasks.getAll();
// [{ key: 'task-1', value: { title: '...', completed: false, assignee: '...' } }]

Update Entry

update({ key, value }) Partially update an existing entry. Only the provided fields are validated and merged:
await client.stores.tasks.update({
  key: 'task-1',
  value: { completed: true },
});
// Only 'completed' is updated; 'title' and 'assignee' remain unchanged

Delete Entry

delete({ key }) Delete an entry by key:
await client.stores.tasks.delete({ key: 'task-1' });

Sync Store

sync() Force a full sync of the store from the server:
await client.stores.tasks.sync();

Events

You can listen to changes made to a store like so:
client.stores.tasks.on('changed', (event) => {
  console.log(event.key);    // 'task-1'
  console.log(event.action); // 'set' | 'update' | 'delete'
  console.log(event.value);  // the new value (or null on delete)
});

// Alternatively, subscribe to a specific key
client.stores.tasks.on('task-1', (value) => {
  console.log('task-1 changed:', value);
});
EventPayloadDescription
changes{ key: string, action: 'set' | 'update' | 'delete', value: T | null }Store changes along with the action
<store-key>TValue as defined in the store schema
Store events fire for changes made by other clients. Your own set/update/delete calls resolve with the new value directly.

Validation

The client validates values against the schema before sending them to the server:
  • set: All required fields (without default) must be provided. Default values are applied for missing fields that have defaults.
  • update: Only provided fields are validated against their schema types.
  • Type checking: Values must match the declared type (string, number, or boolean).
If validation fails, the operation throws an error before making a network request.

Examples

Task Store

import CollabKitClient from '@collab-kit/client';
import { defineStores } from '@collab-kit/utils';

const stores = defineStores({
  tasks: {
    title: { type: 'string', required: true },
    completed: { type: 'boolean', default: false },
    assignee: { type: 'string' },
  },
});

// Listen for changes from other clients
client.stores.tasks.on('changed', ({ key, action, value }) => {
  console.log(`[${action}] ${key}:`, value);
  renderTaskList();
});

// Create a task
await client.stores.tasks.set({
  key: 'task-1',
  value: { title: 'Ship v1' },
});

// Mark complete
await client.stores.tasks.update({
  key: 'task-1',
  value: { completed: true },
});