Askr
Server & APIs

Server Actions

Use server actions with the current published Askr packages.

  • @askrjs/askr0.0.59

How to use server actions

Build the HTTP surface from a router and context-first handlers; bind input and return an explicit response helper from the same boundary.

  1. Import the published @askrjs/askr entrypoint.
  2. Keep server actions configuration next to the component, route, or server composition root that owns it.
  3. Handle pending, unavailable, cancellation, and failure states, then verify the production path.
import { createRouter, created } from '@askrjs/server';

export const router = createRouter().post('/projects', async (context) => {
  const input = await context.bind<CreateProjectInput>();
  const project = await context.dependencies.projects.create(input);
  return created(project);
});

Native action requests

A plain HTML form submit to a page action — no JavaScript enhancement — is what the README calls a native action request: on success the server responds with a 303 redirect to a same-origin matched route, and on validation failure it rerenders the page at 422 with the submitted values and field errors already attached. <ActionForm> is the component that renders that plain form and includes the CSRF token automatically. This path is meant to work end-to-end without client-side JavaScript, which is why the response is a redirect or a rerendered page rather than JSON.

Binding and validation

handleAction(descriptor, handler) pairs an ActionDescriptor from @askrjs/askr/actions with an ActionHandler function; the descriptor defines and validates the input shape, so the handler itself always receives validated input, never a raw FormData or JSON body. ActionHandlerContext gives the handler request, url, params, auth, policies, and an AbortSignal — the same shape as ServerContext minus the response helpers, since an action returns an ActionOutcome instead of a Response directly. When validation fails, the registry reports it as an ActionExecution of kind "invalid" carrying the raw submitted values, a list of Issue objects, and fieldErrors keyed by field name — exactly what a native rerender or an enhanced JSON response needs to show errors next to the right inputs.

Action responses

An ActionHandler returns an ActionOutcome — an optional redirect location, an optional result payload, or both — rather than constructing a Response itself; the framework turns that outcome into the right wire format for whichever kind of request triggered it. defineServerActions({dependencies}, ...entries) captures every handler's dependencies once, up front, and the returned ActionRegistry is frozen — there's no mutation API for adding entries afterward. Enhanced, JavaScript-driven action() calls get back the versioned JSON envelope plus query prefix invalidations mentioned in the README, while unenhanced form posts get the redirect-or-rerender behavior described above.

Page integration

A page route opts into specific actions by listing their descriptors in `actions: [descriptor]`, tying authorization to the page it's declared on. createAskrPageHandler({ registry, actions }) reads that per-page allowlist and only dispatches a request to an action if its descriptor is in the matched page's authorized set — an action defined elsewhere in the registry simply isn't reachable from a page that didn't list it. This keeps action authorization declared next to the page markup that renders the form, instead of living in a separate routing table that has to be kept in sync by hand.