Server functions
Server functions are trusted JavaScript that runs next to your data, never in the browser. They power form submit handlers, dynamic option loading, computed behaviors and page preloads.
The isolation rule
Function source is stored server-side and executed server-side. What reaches the browser is only a reference — the client says "run submit handler of form X", the server looks the code up, runs it in a sandbox and returns the result. Client code can never read (or tamper with) the function body.
Writing a submit handler
Form and widget configs accept function fields. A form submit handler:
async (ctx) => {
const v = ctx.values;
// Validate on the server — the browser is not a trust boundary.
if (!v.title || v.title.length < 3) {
return { ok: false, alert: { type: "error", message: "Title is too short" } };
}
// ctx.datacore: the data API bound to the caller's identity and roles.
await ctx.datacore.table("core", "task").create({
title: v.title,
done: false,
});
return {
ok: true,
alert: { type: "success", message: "Task created" },
navigate: "refresh",
};
}
Loading options dynamically
Select/cardselect fields can compute their options server-side:
async () => {
return [
{ value: "low", label: "Low", icon: "ArrowDown" },
{ value: "high", label: "High", icon: "ArrowUp" },
];
}
Calling database functions
The ctx.datacore.action(schema, fn) helper wraps the
gated RPC: schema USAGE + function EXECUTE
are checked, then the function runs under the caller's database role.
await ctx.datacore.action("core", "iam_client_create").execute({
p_rep: JSON.stringify({ clientId: "my-spa", publicClient: true }),
});
Where server functions appear
| Place | Hook |
|---|---|
| Forms | submit.fn, per-field option loaders, visibility rules |
| Pages | widget preloads, row/page actions |
| Tables | computed columns, bulk actions |