Kizaki
ReferenceTypeScript

@kizaki/sdk/browser

Browser-side query builders, auth functions, and the browser client for live data.

@kizaki/sdk/browser is the browser-side query surface. It provides the same select and filter functions as the server SDK, but builds query objects for one-shot fetches or live subscriptions instead of executing directly.

Query Builders

Browser query builders mirror the server SDK. Build a query with select, apply filters with where, pass the result to a client method or React hook.

import { select, eq } from "@kizaki/sdk/browser";
import { Project } from "@kizaki/schema";

const activeProjects = select(Project)
  .fields(Project.id, Project.name, Project.createdAt)
  .where(eq(Project.ownerId, userId))
  .orderBy(Project.createdAt, "desc")
  .limit(50);

All filter functions are available: eq, neq, gt, gte, lt, lte, like, ilike, inArray, notInArray, isNull, isNotNull, plus and, or, not.

Query builder objects are inert descriptions. They do not execute until passed to a client method or hook.

BrowserKizakiClient

The underlying connection to the platform. Manages WebSocket subscriptions, caching, and presence channels.

Creating a Client

import { createBrowserClient, getDefaultBrowserClient } from "@kizaki/sdk/browser";

// New instance
const client = createBrowserClient();

// Shared default (used automatically by React hooks)
const defaultClient = getDefaultBrowserClient();

Most applications do not need to create a client directly. React hooks and KizakiProvider manage this automatically. Direct access is useful for non-React frameworks or fine-grained control.

Client Methods

MethodPurpose
fetch(builder)One-shot query. Returns { rows, totalCount }
subscribe(builder, listener)Live query. Listener receives { status, rows, error } on every change
getSnapshot(builder)Current cached state without triggering a fetch
invalidate(builder?)Force refetch of a specific query, or all queries if omitted
updateQuery(builder, updater)Optimistic update. Returns a rollback function
joinPresence(channel, id?)Presence tracking. Returns { count, subscribe }
joinBroadcast(channel, id?)Ephemeral messaging. Returns { send, lastMessage, subscribe }
close()Disconnect the WebSocket and release resources

One-Shot Fetch

const { rows, totalCount } = await client.fetch(
  select(Project).fields(Project.id, Project.name).limit(10)
);

Live Subscription

import { createBrowserClient, select } from "@kizaki/sdk/browser";
import { Message } from "@kizaki/schema";

const client = createBrowserClient();

const sub = client.subscribe(
  select(Message)
    .fields(Message.id, Message.text, Message.createdAt)
    .orderBy(Message.createdAt, "desc"),
  (state) => {
    if (state.status === "loading") showSpinner();
    if (state.status === "success") renderMessages(state.rows);
    if (state.status === "error") showError(state.error);
  }
);

// Later:
sub.unsubscribe();

The listener fires immediately with cached state (if any), then again on every server-side change via WebSocket.

Optimistic Updates

Update cached state before the server confirms. updateQuery returns a rollback function.

const rollback = client.updateQuery(
  select(Project).orderBy(Project.createdAt, "desc"),
  (current) => ({
    ...current,
    rows: [{ id: "temp", name: "New Project", createdAt: new Date() }, ...current.rows],
  })
);

// If the server mutation fails:
rollback();

Auth Functions

Browser auth helpers are re-exported from @kizaki/sdk/auth:

  • login, signup, logout, getSession, getSessionSync, refreshSession
  • getAuthConfig
  • loginWithGoogle, loginWithGithub, loginWithProvider
  • forgotPassword, resetPassword
  • onAuthStateChange
  • listAuthMethods, linkPassword, linkProvider, unlinkMethod

See the Auth reference for full documentation.

Browser Integration Helpers

Managed integration lifecycle helpers:

  • listIntegrations()
  • getIntegrationStatus(key)
  • beginIntegrationConnect(key, returnTo?)
  • beginIntegrationUpgrade(key, "write", returnTo?)
  • disconnectIntegration(key)

These manage connection lifecycle only. They return status and redirect into provider consent flows but never expose provider access tokens to browser code.

Provider-specific operation helpers like googleCalendar("calendar").getFreeBusy(...) are server-only and live in @kizaki/google.

import {
  listIntegrations,
  beginIntegrationConnect,
  beginIntegrationUpgrade,
} from "@kizaki/sdk/browser";

const integrations = await listIntegrations();

if (!integrations.find((item) => item.key === "calendar")?.connected) {
  await beginIntegrationConnect("calendar", "/settings");
}

await beginIntegrationUpgrade("calendar", "write", "/settings");

See Inspire Integrations for the schema-side declaration.

Presence

Presence channels track connected user counts for a shared context (document, room, dashboard).

const presence = client.joinPresence("document", docId);

// Current count
console.log(presence.count);

// Subscribe to changes
const unsub = presence.subscribe((count) => {
  updateViewerCount(count);
});

// Later:
unsub();

Broadcast

Broadcast channels send ephemeral messages to all connected users on a channel. Messages are not persisted. Common uses: cursor positions, typing indicators, collaborative selections.

const broadcast = client.joinBroadcast("document", docId);

// Send to all other users
broadcast.send({ type: "cursor", x: 100, y: 200 });

// Most recent incoming message
console.log(broadcast.lastMessage);

// Subscribe to incoming messages
const unsub = broadcast.subscribe((message) => {
  if (message.type === "cursor") drawRemoteCursor(message);
});

// Later:
unsub();

Broadcast is fire-and-forget. Disconnected users do not receive missed messages.

Related guide: Realtime

On this page