WebJs Gallery
← Gallery

Server actions

A 'use server' action is RPC-callable from the client; a plain .server.ts is a server-only utility you never import into a component.

This action also declares export const middleware: a chain that runs around it on every boundary. The auth middleware reads the real signed session (from the auth card) and sets the caller on the request context (read back with actionContext()), or 401s before the action runs. The action threads actionSignal(), the request AbortSignal, through its work so a client disconnect or a superseded render stops it early.

Signed out, the greeter returns a real 401. Sign in first to see it succeed. (This card depends on the auth card; prune both together.)

The action is gated by requireAuth. Sign in to greet; signed out returns a real 401.

HTTP verbs and caching

An action declares its HTTP semantics through reserved sibling exports the framework reads statically, the same way a page declares export const revalidate. The read below sets method = 'GET', so its args ride the URL, it is CSRF-exempt, and it carries a weak ETag, so a revalidated read whose result has not changed answers 304. Caching itself is opted into by the cache export below, not by the verb: a GET without one is no-store. An action with no method export is a POST mutation. Seeding is a separate mechanism and needs no verb: an action invoked during a fully buffered SSR render has its result serialized into the page, so the first client call reads that seed instead of making a hydration round-trip. A page that streams (a Suspense or <webjs-suspense> boundary) emits no seed block, so its actions do call out on hydration.

cache = 10 is the max-age in seconds, and it is private by default. Reach for { public: true } only when the data is identical for every visitor, because a shared cache keys the entry on the URL and args alone. That is the same safety rule as a page's export const revalidate. The number is shorthand for the object form, so cache = { maxAge: 10, swr: 30 } keeps serving an expired entry for another thirty seconds while the browser revalidates it in the background. There is no separate swr export.

tags labels the cached entry and invalidates on the mutation evicts it by name. The read reports how many times it actually ran on the server, so press Read twice inside ten seconds and that count does not move: the second answer came from the browser cache without reaching the server. Then bump the counter and read again. The count moves and the value is fresh, because the mutation reported its invalidated tag and the next read bypassed the stale entry instead of waiting out the window.

Press Read twice in a row, then bump and read again.