Backend logic in TypeScript.
Queries, mutations, and actions with v.* validators. The filename is the RPC name. Call them from React with a typed client — or let writes flow through the sync engine and broadcast automatically.
import { mutation, v } from "@pylonsync/functions";
export default mutation({
args: { roomId: v.id("Room"), body: v.string() },
async handler(ctx, args) {
if (!ctx.auth.userId) throw ctx.error("UNAUTHENTICATED", "log in first");
const id = await ctx.db.insert("Message", {
roomId: args.roomId,
authorId: ctx.auth.userId,
body: args.body.trim(),
createdAt: new Date().toISOString(),
});
return ctx.db.get("Message", id); // broadcasts to subscribers
},
});- query / mutation / action with validated, typed arguments
- Filename = RPC name — functions/sendMessage.ts is callable as sendMessage
- ctx.db honors policies; ctx.auth carries the caller; ctx.error throws typed failures
- Writes emit change events, so live queries update with zero extra wiring
- Test logic AND components with `pylon test` — Bun + @testing-library, wired by default
Validated arguments, end to end
v.string(), v.id("Room"), v.object({…}) describe the shape once. Pylon validates inbound calls against it and infers the handler's argument types — so a typo'd field is a type error at your desk, not a 500 in production.
The context object is the whole backend
ctx.db reads and writes through the policy engine using the caller's identity. ctx.auth carries the user and tenant. ctx.error throws structured errors the client can switch on. ctx.schedule enqueues background work; ctx.llm calls a model. The ORM, the job queue, and the auth layer are already part of the context.
Writes are live by default
Every mutation and action that writes emits typed change events. Any db.useQuery watching affected rows updates instantly — you don't publish to a channel, invalidate a cache, or re-fetch. The function just returns the row.
Test it without standing anything up
Keep decisions — gating, pricing, validation — in plain functions and test them with `pylon test`, Bun’s runner against an in-memory Pylon. Component tests render with @testing-library + happy-dom, wired into every new app by default. No fixtures, no test database, no mocking the framework.
Build it on Pylon.
One framework for your schema, sync, auth, functions, realtime, and SSR. Free to start.