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;
}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
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.