Reference
Routes and context
For your agentCopy this and give it to the AI you're building with.
Run `cubek skill`, read all of it and follow it exactly (no CLI: download https://cubek.dev/SKILL.md verbatim with curl; don't summarize it); when you write routes and handlers, follow its "Routes and context" section. Run `cubek check` after every edit until it is clean.
import { app, crypto, db, validate, type Context } from "cubek"
import { Home } from "./pages/home"
app.get("/", async (c) => c.render(<Home notes={await db.notes.find({ limit: 20 })} />))
app.get("/api/notes/:id", async (c) => {
const note = await db.notes.get(c.param("id"))
return note === null ? c.notFound() : c.json(note)
})
const noteInput = validate.object({ title: validate.string({ min: 1, max: 200 }), body: validate.string() })
app.post("/notes", async (c) => {
const parsed = noteInput.safeParse(await c.body()) // JSON, urlencoded or multipart; malformed: 400 for you
if (!parsed.success) return c.json({ error: parsed.error }, 400)
const note = await db.notes.insert(parsed.data)
return c.redirect(`/notes/${note.id}`, 303) // 303 after a POST
})
app.use("/admin/*", async (c, next) => c.header("x-admin") === "yes" ? next() : c.text("forbidden", 403))app.get/post/put/patch/delete(path, [middleware,] handler),app.use([path,] ...middleware)(every matching path, any order). The most specific route wins (/tags/new>/tags/:id>/*). Paths are literals at top level (no loops): one route each, or a param (/mod/:action).c.param/query/header/cookie(name)→string | null(Number(c.query("page") ?? "1"));c.url(),c.path()(no query),c.origin()(absolute links: RSS, email),c.ip()(not spoofable),c.preview(null in prod: deploy.md);await c.body<T>()is unvalidated;await c.rawBody()→ exactBytes; file fields:{ name, type, size, bytes }(storage.md).- Responses (
type Response):c.json/text/html(body, status?, headers?)(headers replace the content-type),c.body(bytes, status?, headers?),c.render(jsx, status?);c.status(code),c.setHeader(before or afterc.render). - Middleware:
rateLimit({ limit: 20, window: "1m" }),cors({ origin })(http.md). - Forms send strings:
validate.number({ coerce: true }),validate.boolean({ coerce: true });tags[]fields →string[]; repeated:await c.formAll("ids"). - A thrown
Erroris an empty 500 (details:cubek devoutput,cubek logs).