Write Server Functions
Expose server-side workflows with typed functions and let Kizaki generate the client layer for you.
Add /** @expose */ to any exported async TypeScript function under src/ to make it callable from your app. src/functions.ts is the starter file, but Kizaki scans all supported source files, validates them against your schema, and generates @kizaki/client.
The browser calls typed exposed functions. Those functions 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;
}Recommended Pattern
- 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
During local development, @kizaki/client and @kizaki/schema refresh automatically. The VS Code extension also keeps editor types current once you grant workspace trust.
A good exposed function encodes a business action: create an order, invite a teammate, start a checkout, publish a post, rotate an API key. Think in terms of what the UI is trying to accomplish, not raw CRUD.
Transactions
When a workflow must be atomic, use getContext() and client.transaction(...) inside the server function. This keeps your browser API simple and your data consistent.
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:
- exposed functions for mutations and business workflows
- browser query objects for read-heavy UI state
Continue with Query Data In The Browser.