Kizaki
Learn

Query Data In The Browser

Use the generated client for mutations and the browser query stack for typed reads.

Kizaki gives browser apps two complementary tools:

  • @kizaki/client for calling exposed server functions
  • @kizaki/sdk/browser and @kizaki/react for browser-side query objects and live reads

The easiest way to stay sane is to assign each tool a clear job.

Use The Generated Client For Workflows

import { createProject } from "@kizaki/client";

await createProject("Docs rewrite");

This is the recommended path for mutations and business workflows.

If an action changes the system, performs validation, coordinates multiple writes, or should stay behind a single typed entrypoint, it usually belongs behind an exposed function and the generated client.

Use Query Objects For Browser Reads

import { select } from "@kizaki/sdk/browser";
import { useQuery } from "@kizaki/react";
import { Project } from "@kizaki/schema";

const projectsQuery = select(Project)
  .fields(Project.id, Project.name)
  .orderBy("createdAt", "desc");

export function ProjectsList() {
  const projects = useQuery(projectsQuery);

  if (projects.status === "loading") return <p>Loading...</p>;
  if (projects.status === "error") return <p>{projects.error?.message}</p>;

  return (
    <ul>
      {projects.data.map((project) => (
        <li key={project.id}>{project.name}</li>
      ))}
    </ul>
  );
}
  • use generated client calls for actions
  • use query objects for data shown in the UI
  • let invalidation and live updates keep the browser state current

This gives you a clean architecture:

  • browser code stays simple
  • server workflows stay centralized
  • reads can stay reactive without inventing a second API surface

If you already know a screen should stay live, build the browser read with a query object from the start. You can fetch it once today and keep it live later without rewriting the UI data model.

Continue with Add Realtime.

On this page