Functions
Functions are the server-side logic of your Zeroback application. They are defined in .ts files inside the zeroback/ directory and are automatically discovered and bundled by the CLI.
Function Types
Section titled “Function Types”| Type | Can read DB | Can write DB | Can call APIs | Callable from client | Real-time |
|---|---|---|---|---|---|
query | Yes | No | No | Yes | Yes (subscriptions) |
mutation | Yes | Yes | No | Yes | No |
action | No (via runQuery) | No (via runMutation) | Yes | Yes | No |
internalQuery | Yes | No | No | No (server only) | No |
internalMutation | Yes | Yes | No | No (server only) | No |
internalAction | No (via runQuery) | No (via runMutation) | Yes | No (server only) | No |
Defining Functions
Section titled “Defining Functions”Import the typed function factories from zeroback/_generated/server (generated by codegen):
import { query, mutation, action } from "./_generated/server";import { internalQuery, internalMutation, internalAction } from "./_generated/server";import { v } from "@zeroback/server";query(config)
Section titled “query(config)”Defines a read-only query. Queries are reactive — clients subscribing to a query receive real-time updates when the underlying data changes.
query({ args: { /* validators */ }, returns?: Validator, handler: async (ctx, args) => { /* ... */ },})| Field | Type | Required | Description |
|---|---|---|---|
args | Record<string, Validator> | Yes | Argument validators. Validated at runtime. |
returns | Validator | No | Return type validator. If set, the return value is validated at runtime. |
handler | (ctx: QueryCtx, args) => Promise<T> | Yes | The query function. |
QueryCtx provides:
ctx.db—DatabaseReader(read-only database access)ctx.storage—StorageReader(read-only file storage access)
Example:
export const listByProject = query({ args: { projectId: v.string() }, handler: async (ctx, args) => { return await ctx.db .query("tasks") .withIndex("by_project", (q) => q.eq("projectId", args.projectId)) .order("desc") .take(100); },});mutation(config)
Section titled “mutation(config)”Defines a function that can read and write to the database. Mutations are transactional — they run inside an OCC transaction that automatically retries on conflict.
mutation({ args: { /* validators */ }, returns?: Validator, handler: async (ctx, args) => { /* ... */ },})| Field | Type | Required | Description |
|---|---|---|---|
args | Record<string, Validator> | Yes | Argument validators |
returns | Validator | No | Return type validator |
handler | (ctx: MutationCtx, args) => Promise<T> | Yes | The mutation function |
MutationCtx provides:
ctx.db—DatabaseWriter(read + write database access)ctx.scheduler—Scheduler(schedule future function calls)ctx.storage—StorageWriter(read + write file storage access)
Example:
export const create = mutation({ args: { title: v.string(), status: v.string(), projectId: v.string(), }, handler: async (ctx, args) => { return await ctx.db.insert("tasks", args); },});action(config)
Section titled “action(config)”Defines a function that can call external APIs and invoke other functions. Actions do not have direct database access — they call queries and mutations via ctx.runQuery() and ctx.runMutation().
action({ args: { /* validators */ }, returns?: Validator, handler: async (ctx, args) => { /* ... */ },})| Field | Type | Required | Description |
|---|---|---|---|
args | Record<string, Validator> | Yes | Argument validators |
returns | Validator | No | Return type validator |
handler | (ctx: ActionCtx, args) => Promise<T> | Yes | The action function |
ActionCtx provides:
ctx.runQuery(fnName, args?)— Call a query functionctx.runMutation(fnName, args?)— Call a mutation functionctx.runAction(fnName, args?)— Call another action functionctx.scheduler—Schedulerctx.storage—StorageActions(full storage access includingstore())
The fnName format is "module:functionName" — e.g., "tasks:create", "utils/stats:taskStats".
Example:
export const createViaAction = action({ args: { title: v.string(), status: v.string(), projectId: v.string(), }, handler: async (ctx, args) => { await ctx.runMutation("tasks:create", args); const tasks = await ctx.runQuery("tasks:listByProject", { projectId: args.projectId, }); return { created: true, count: tasks.length }; },});internalQuery(config) / internalMutation(config) / internalAction(config)
Section titled “internalQuery(config) / internalMutation(config) / internalAction(config)”Same signature as their public counterparts, but cannot be called from the client. They can only be invoked server-side via ctx.runQuery(), ctx.runMutation(), ctx.runAction(), or via the scheduler.
export const countInternal = internalQuery({ args: { projectId: v.string() }, handler: async (ctx, args) => { const tasks = await ctx.db .query("tasks") .withIndex("by_project", (q) => q.eq("projectId", args.projectId)) .collect(); return tasks.length; },});Function Naming
Section titled “Function Naming”Functions are named based on their file path and export name:
| File | Export | Function Name |
|---|---|---|
zeroback/tasks.ts | create | "tasks:create" |
zeroback/tasks.ts | listByProject | "tasks:listByProject" |
zeroback/utils/stats.ts | taskStats | "utils/stats:taskStats" |
Files that are excluded from function discovery:
zeroback/schema.tszeroback/_generated/*- Files starting with
_
Return Value Validation
Section titled “Return Value Validation”Use the optional returns field to validate the return value at runtime:
export const countByProject = query({ args: { projectId: v.string() }, returns: v.number(), handler: async (ctx, args) => { const tasks = await ctx.db .query("tasks") .withIndex("by_project", (q) => q.eq("projectId", args.projectId)) .collect(); return tasks.length; },});HTTP Actions
Section titled “HTTP Actions”HTTP actions expose your functions as REST endpoints. Define them in a file that exports a default HttpRouter.
httpRouter()
Section titled “httpRouter()”Creates a new HTTP router instance.
import { httpRouter, httpAction } from "@zeroback/server";
const http = httpRouter();// ... register routes ...export default http;httpAction(handler)
Section titled “httpAction(handler)”Wraps a handler function for use with the router.
function httpAction( handler: (ctx: ActionCtx, request: Request) => Promise<Response>): HttpAction| Parameter | Type | Description |
|---|---|---|
ctx | ActionCtx | Action context with runQuery, runMutation, runAction, scheduler, storage |
request | Request | Standard Web API Request object |
Must return a standard Web API Response object.
HttpRouter.route(config)
Section titled “HttpRouter.route(config)”Register an exact-path route.
http.route({ path: string, method: string, handler: HttpAction,})| Field | Type | Description |
|---|---|---|
path | string | Exact path to match (e.g., "/api/tasks") |
method | string | HTTP method ("GET", "POST", "PUT", "DELETE", etc.) |
handler | HttpAction | An httpAction() wrapper |
HttpRouter.routeWithPrefix(config)
Section titled “HttpRouter.routeWithPrefix(config)”Register a prefix-based route. Matches any path that starts with the given prefix.
http.routeWithPrefix({ pathPrefix: string, method: string, handler: HttpAction,})Exact routes take priority over prefix routes when both match.
Full Example
Section titled “Full Example”import { httpRouter, httpAction } from "@zeroback/server";
const http = httpRouter();
http.route({ path: "/api/tasks", method: "GET", handler: httpAction(async (ctx, request) => { const url = new URL(request.url); const projectId = url.searchParams.get("projectId") ?? ""; const tasks = await ctx.runQuery("tasks:listByProject", { projectId }); return new Response(JSON.stringify(tasks), { headers: { "Content-Type": "application/json" }, }); }),});
http.route({ path: "/api/tasks", method: "POST", handler: httpAction(async (ctx, request) => { const body = await request.json(); await ctx.runMutation("tasks:create", body); return new Response(JSON.stringify({ ok: true }), { headers: { "Content-Type": "application/json" }, }); }),});
export default http;Cron Jobs
Section titled “Cron Jobs”Cron jobs run functions on a recurring schedule. Define them in a file that exports a default CronJobs instance.
cronJobs()
Section titled “cronJobs()”Creates a new cron jobs instance.
import { cronJobs } from "@zeroback/server";
const crons = cronJobs();// ... register jobs ...export default crons;crons.interval(name, schedule, fnName, args?)
Section titled “crons.interval(name, schedule, fnName, args?)”Run a function at a fixed interval.
crons.interval( name: string, schedule: { hours?: number; minutes?: number; seconds?: number }, fnName: string, args?: unknown)| Parameter | Type | Default | Description |
|---|---|---|---|
name | string | — | Unique job name |
schedule | object | — | Interval duration. At least one field must be positive. |
schedule.hours | number | 0 | Hours component |
schedule.minutes | number | 0 | Minutes component |
schedule.seconds | number | 0 | Seconds component |
fnName | string | — | Function to call (e.g., "tasks:cleanup") |
args | unknown | {} | Arguments to pass to the function |
crons.cron(name, expression, fnName, args?)
Section titled “crons.cron(name, expression, fnName, args?)”Run a function on a standard 5-field cron schedule (UTC).
crons.cron(name: string, expression: string, fnName: string, args?: unknown)| Parameter | Type | Default | Description |
|---|---|---|---|
name | string | — | Unique job name |
expression | string | — | 5-field cron expression: "minute hour dayOfMonth month dayOfWeek" |
fnName | string | — | Function to call |
args | unknown | {} | Arguments to pass |
crons.hourly(name, opts?, fnName, args?)
Section titled “crons.hourly(name, opts?, fnName, args?)”Run a function once per hour.
| Parameter | Type | Default | Description |
|---|---|---|---|
opts.minuteUTC | number | 0 | Minute of the hour (0-59) |
crons.daily(name, opts?, fnName, args?)
Section titled “crons.daily(name, opts?, fnName, args?)”Run a function once per day.
| Parameter | Type | Default | Description |
|---|---|---|---|
opts.hourUTC | number | 0 | Hour of the day (0-23) |
opts.minuteUTC | number | 0 | Minute of the hour (0-59) |
crons.weekly(name, opts?, fnName, args?)
Section titled “crons.weekly(name, opts?, fnName, args?)”Run a function once per week.
| Parameter | Type | Default | Description |
|---|---|---|---|
opts.dayOfWeek | number | 1 (Monday) | Day of week (0=Sun, 1=Mon, …, 6=Sat) |
opts.hourUTC | number | 0 | Hour of the day |
opts.minuteUTC | number | 0 | Minute of the hour |
crons.monthly(name, opts?, fnName, args?)
Section titled “crons.monthly(name, opts?, fnName, args?)”Run a function once per month.
| Parameter | Type | Default | Description |
|---|---|---|---|
opts.dayOfMonth | number | 1 | Day of the month (1-31) |
opts.hourUTC | number | 0 | Hour of the day |
opts.minuteUTC | number | 0 | Minute of the hour |
Cron Example
Section titled “Cron Example”import { cronJobs } from "@zeroback/server";
const crons = cronJobs();
// Every 5 secondscrons.interval("cleanup", { seconds: 5 }, "tasks:cleanupDone", {});
// Every day at 9:00 UTCcrons.daily("digest", { hourUTC: 9 }, "email:sendDigest");
// Every Monday at 8:00 UTCcrons.weekly("report", { dayOfWeek: 1, hourUTC: 8 }, "reports:weekly");
// 1st of every month at midnightcrons.monthly("billing", { dayOfMonth: 1 }, "billing:charge");
// Custom cron expression (weekdays at 3:00 AM UTC)crons.cron("nightly", "0 3 * * 1-5", "jobs:nightly");
export default crons;Codegen Output
Section titled “Codegen Output”Running zeroback dev, zeroback deploy, or zeroback codegen generates three files in zeroback/_generated/:
api.ts
Section titled “api.ts”Typed function references for the client. Each function becomes a property on the api object:
import { api } from "../zeroback/_generated/api";
// api.tasks.create — FunctionReference<"mutation", { title: string, ... }>// api.tasks.listByProject — FunctionReference<"query", { projectId: string }>Internal functions are available on the internal object:
import { internal } from "../zeroback/_generated/api";
// internal.tasks.countInternal — FunctionReference<"query", { projectId: string }>server.ts
Section titled “server.ts”Typed function factories bound to your DataModel:
import { query, mutation, action } from "./_generated/server";import { internalQuery, internalMutation, internalAction } from "./_generated/server";These provide full type safety — ctx.db.query("tasks") knows the shape of your tasks table.
dataModel.ts
Section titled “dataModel.ts”The DataModel type mapping table names to their document types:
type DataModel = { tasks: { _id: string; _creationTime: number; title: string; status: string; // ... }; // ...};