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.
- Import the published @askrjs/askr entrypoint.
- Keep server actions configuration next to the component, route, or server composition root that owns it.
- 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.