Quickstart
Create a new Kizaki app, run it locally, and make your first authenticated change in under ten minutes.
This guide uses the React Router SaaS template, which is the default and the fastest way to learn the platform.
By the end of this page, you will have:
- a scaffolded Kizaki app
- a running local development stack
- an authenticated browser session
- a generated client call going from the frontend to your server code
1. Create A New App
kizaki init hello-kizaki
cd hello-kizaki
npm installYou will get a starter app with:
- an Inspire schema in
schema/main.inspire - server functions in
src/functions.ts - a React Router frontend
- a committed baseline migration
2. Start The Local Stack
kizaki devExpected flow:
- Kizaki checks the toolchain
- boots embedded Postgres
- applies committed migrations
- starts the runtime and local app
- opens up browser-ready HTTP and WebSocket endpoints
When startup completes:
- keep the terminal running
- open the local app URL from the output
- treat that terminal as the control center for the app while you work
The important thing to understand is that kizaki dev is not only a web server. It is the coordinated local environment for your schema, runtime, generated client, and browser app.
3. Sign In Locally
The SaaS template ships with email auth enabled. In local development, use the built-in dev login flow and create a session for yourself.
Once you are logged in, the starter app will show your personal todos.
That is your first proof that the default stack is wired correctly:
- auth is active
- the current principal is available in server code
- the generated client can call exposed functions
- the UI can render data scoped to the signed-in user
4. Create A Todo
The template already includes exposed server functions and a generated client. The initial server code looks like this:
import { getPrincipal, insert, query, select, eq } from "@kizaki/sdk";
import { Todo } from "@kizaki/schema";
/** @expose */
export async function listTodos() {
const me = getPrincipal();
return query(
select(Todo)
.fields(Todo.id, Todo.title, Todo.completed)
.where(eq(Todo.ownerId, me.id)),
);
}
/** @expose */
export async function createTodo(title: string): Promise<Todo> {
const me = getPrincipal();
const [todo] = await query(
insert(Todo)
.values({ title, ownerId: me.id })
.returning(),
);
return todo;
}From the frontend, the generated client is called directly:
import { createTodo } from "@kizaki/client";
await createTodo("Ship the first version");This is the central Kizaki loop:
- write server logic in
src/functions.ts - mark it with
/** @expose */ - let the compiler generate a typed client
- call that generated client from the app
You do not hand-author a REST layer for this path.
5. What To Learn Next
Follow the recommended path: