Server functions v0.8
A module with a top-of-file "use server" directive becomes an RPC boundary. Its exports run only on the server; the browser gets a tiny fetch proxy.
Write one
// actions/todos.ts
"use server";
import { getContext } from "@nowaki-dev/runtime/server/functions.mjs";
const store: string[] = [];
export async function addTodo(text: string) {
store.push(text.trim());
return store.slice();
}
export async function whoami() {
const ctx = getContext(); // request context, RPC-time only
return ctx?.cookies?.user ?? "anonymous";
}Call it from an island
Import and call it like a normal async function. The browser only ships the proxy.
// islands/Todos.tsx
import { useState } from "preact/hooks";
import { addTodo } from "../actions/todos.ts";
export default function Todos({ initial = [] }) {
const [todos, setTodos] = useState(initial);
return <button onClick={async () => setTodos(await addTodo("new"))}>add</button>;
}On the server you can call the same function directly (no HTTP) — e.g. from a routeloader — so server-to-server calls stay in-process.
How it works
- The build strips the implementation (and its server-only imports) from the client bundle and emits a proxy that posts
{ id, args }to/__nowaki/fn. - Each export gets a stable id (a hash of
module#export) computed identically on both sides. - Dispatch is allowlisted: the server maps id → { module, export } from a build-time table. A client can't reach an arbitrary export.
getContext()exposes the request context (cookies, headers) via AsyncLocalStorage during the call.- Works in dev,
nowaki start, and on edge adapters.
Treat each server function like a public HTTP endpoint: validate its arguments(they arrive as JSON from the client) and check authorization with
getContext(). Errors return only error.message to the client, never the stack.Authoring rules
- Server modules live under
routes/,islands/,components/,lib/, oractions/. - Use explicit file extensions in imports (
../actions/todos.ts). - Exports should be async functions that take JSON-serializable arguments and return JSON-serializable values.