Quickstart
Scaffold a project, start the worker runtime, and serve your first route. Two minutes, no configuration files.
Scaffold a project
The CLI writes a complete project — src/main.ts, the yatta/func/ directory, and every subsystem already wired to its own worker pool. It then runs bun install for you.
bunx yatta new my-appcd my-appbun run devYou should see the runtime mount all eight subsystems and bind a port:
✓ Mounted 8 subsystems (auth, jobs, db, cache, mail, storage, events, cron)Yatta server running at http://localhost:4000 (PID: 4711)Write your first route
Routes are files. Drop one in yatta/backend/ and it is served on the next request — there is no router to register it with.
import { createAPI } from "yatta/api"; const hello = createAPI("/hello"); hello.get((ctx) => { return Response.json({ message: "Hello from Yatta" });}); export default hello;$ curl http://localhost:4000/hello{"message":"Hello from Yatta"}The path passed to createAPI is typed, so ctx.params is inferred from the literal rather than declared by hand.
Add data
Declare a schema and the ORM builds the tables on boot. There is no migration file to write for a new schema.
import { col, createDatabase } from "yatta/db"; export const schema = { notes: { id: col.uuid(), title: col.text(), body: col.text().nullable(), createdAt: col.createdAt(), },}; export const db = createDatabase({ url: process.env.DATABASE_URL, schema,});import { createAPI } from "yatta/api";import { db } from "../func/db"; const notes = createAPI("/notes"); // Tables are properties on the handle: db.notes, not a select().from().notes.get(async () => { const rows = await db.notes .where((f) => f.archived.isEqualTo(false)) .orderBy({ createdAt: "desc" }) .limit(50) .all(); return Response.json(rows);}); notes.post(async (ctx) => { const body = await ctx.json(); const note = await db.notes.insert(body); return Response.json(note, { status: 201 });}); export default notes;Move slow work off the request
Anything slow — sending mail, calling a third-party API, generating a report — belongs on a job. The handler runs on a worker, so the request returns as soon as the job is queued.
import { createJobs } from "yatta/jobs"; export const jobs = createJobs(); export interface JobHandlers { "send-welcome": { to: string };} jobs.handle("send-welcome", async ({ to }) => { await mail.send({ to, subject: "Welcome", template: "welcome" });});await jobs.enqueue("send-welcome", { to: user.email });Check the build
yatta check runs the same validation in CI — TypeScript across the project, plus a boot of the runtime to prove the schema applies cleanly.
bun run yatta checkyatta check before every deploy. It catches the two failures that are otherwise only visible in production: a schema that will not apply, and a worker that cannot mount.Every command
yatta new <name>— scaffold a project.yatta new .scaffolds into the current directory.yatta dev— start with hot reload.yatta start— start in production mode.yatta build— compile for deployment.yatta cluster— spawn one worker process per core, bound to the same port withSO_REUSEPORT.yatta check— typecheck and boot-verify.yatta info— print the resolved environment and topology.yatta link/yatta unlink— manage the global link to the framework itself.