Kizaki
Learn

Write Server Functions

Expose server-side workflows with typed functions and let Kizaki generate the client layer for you.

Use /** @expose */ on TypeScript functions in src/functions.ts to make them callable from your app. Kizaki scans those functions, validates them against your schema, and generates @kizaki/client.

This is the recommended boundary for application workflows. The browser should call typed exposed functions. Those functions should use the generated schema objects and the server-side SDK to interact with data.

A Typical Exposed Function

import { Project } from "@kizaki/schema";
import { getPrincipal, insert, query, select, eq } from "@kizaki/sdk";

/** @expose */
export async function listProjects() {
  const me = getPrincipal();
  return query(
    select(Project)
      .fields(Project.id, Project.name)
      .where(eq(Project.ownerId, me.id))
      .orderBy("createdAt", "desc"),
  );
}

/** @expose */
export async function createProject(name: string): Promise<Project> {
  const me = getPrincipal();
  const [project] = await query(
    insert(Project)
      .values({ name, ownerId: me.id })
      .returning(),
  );
  return project;
}
  • use getPrincipal() for the current user
  • use query() for reads and writes
  • use .fields() on reads to return only what the browser needs
  • keep multi-step workflows in server functions, not the frontend

That last point matters. A good exposed function is not just CRUD. It is the place where you encode the business action your UI is trying to perform: create an order, invite a teammate, start a checkout, publish a post, rotate an API key.

Transactions

When a workflow must be atomic, reach for getContext() and client.transaction(...) inside the server function. This keeps your browser API simple and your data flow safe.

When Not To Expose A Function

Do not create exposed functions just to proxy data the browser can already read with query objects. The better split is usually:

  • exposed functions for mutations and business workflows
  • browser query objects for read-heavy UI state

Continue with Query Data In The Browser.

On this page