Actions
Actions are the smallest unit of behavior in Arbor. A route is a list of actions. Arbor runs them in order until one of them:
- returns a value
- throws an error
- finalizes the response
All routing, body parsing, cookies, context and helpers are provided through the Bundle, which is covered in detail in the Request Bundle section.
Actions run sequentially. Arbor executes them in the order it discovers them:
application-level actions → branch actions → route actions.
Creating an Action
Use createAction to define an action. It receives the request bundle as its only argument.
import { createAction } from "@kequtech/arbor";
const actionSetHeader = createAction(({ res }) => {
res.setHeader("X-Powered-By", "arbor");
});
Actions may be synchronous or asynchronous; Arbor awaits each one before continuing.
Adding Actions to a Route
A route executes its actions in the order you provide them:
import { createRoute } from "@kequtech/arbor";
const routeStatus = createRoute({
method: "GET",
url: "/status",
actions: [
actionSetHeader,
() => "OK",
],
});
The second action here returns a value. Arbor selects a renderer and finalizes the response, skipping all remaining actions.
Returning a Value
Returning any non-undefined value ends the action chain:
const actionReply = createAction(() => {
return { status: "ready" };
});
Once an action returns a value:
- Arbor selects a renderer based on the current
Content-Type - The renderer formats the value
- The response is finalized
- No further actions run
Returning undefined means “continue to the next action”.
Throwing an Error
Throwing stops the chain immediately.
const actionCheck = createAction(() => {
throw Ex.BadRequest("Invalid");
});
Arbor chooses an error handler based on the current Content-Type and formats the error accordingly. All thrown errors are normalized to ServerEx objects (see Errors section).
Finalizing the Response
An action may manually finalize the response:
const actionRedirect = createAction(({ res }) => {
res.statusCode = 302;
res.setHeader("Location", "/login");
res.end();
});
Calling res.end() stops the chain. No renderer or error handler is invoked afterward. Manual finalization is useful for redirects, file streaming, or custom protocols.
Using the Request Bundle
See the Request Bundle page for the full description and best practices.
const actionAuth = createAction(({ req, context }) => {
const token = req.headers.authorization;
if (!token) {
throw Ex.Unauthorized();
}
context.user = token;
});
Actions are encouraged to:
- modify
res - read request information
- store data in
context - parse the body using
getBody - enforce security and validation
- return data to invoke a renderer
- throw errors
Types and Hints
Arbor does not try to statically type entire request pipelines. Actions, however, can use TypeScript hints to make their assumptions explicit.
Two forms of hints are supported:
- context hints
- return type hints
These hints improve local clarity inside actions without changing Arbor’s runtime model.
// context hint
export default createAction<Context>(({ context }) => {
// context is typed
});
// return type hint
export default createAction((): Payload => ({ status: "ok" }));
For a more complete explanation of these patterns and when to use them, see Types.
Summary
Actions form a clear, predictable execution model:
- run in order
- return → render and stop
- throw → error handler and stop
- finalize → stop
- otherwise → continue
Together, actions and routes define how each request in an Arbor application behaves.