Handlers
Write the code that runs when someone calls one of your actions.
A handler is the code that runs when an action is called. You register it on the runtime and it answers incoming calls. Every style receives a second, per-invocation ctx argument.
Exhaustive domain map (recommended)
Section titled “Exhaustive domain map (recommended)”const userHandler = act_user.wrapAsLocalHandler({ getUser: async ({ userId }, ctx) => { const user = await db.users.find(userId); if (!user) throw err_user.fromId("not_found", { userId }); return user; }, updateName: async ({ userId, name }) => { await db.users.update(userId, { name }); return { success: true }; },});This form is exhaustive: adding an action to the domain makes TypeScript ask for its implementation. Each callback receives the typed input directly.
ctx.runtime is the exact runtime executing the callback, and ctx.run(request, options) dispatches a
nested action through that runtime. ctx.origin is the request’s claimed origin;
ctx.authenticatedOrigin is present only when the transport authenticated it. ctx.securityLevel and
ctx.reliability expose the receiver-owned security and delivery facts. A nested action gets its own
context; connection or authorization facts are never copied into it.
When you use serveChannel({ channelCases }), the same context also has connection, typed state,
setState, clearState, pushBack, and broadcast. Socket-only writes throw connection_required on
an HTTP request, while connection/state reads are nullable.
Partial case map
Section titled “Partial case map”Use forDomainActionCases when one handler deliberately covers only part of a domain and you want the
full primed action:
import { createLocalHandler } from "@nice-code/action";
const userHandler = createLocalHandler().forDomainActionCases(act_user, { getUser: async (action, ctx) => { return db.users.find(action.input.userId); },});One action at a time
Section titled “One action at a time”const userHandler = createLocalHandler() .forAction(act_user.action.getUser, async ({ input }, ctx) => db.users.find(input.userId));Handling only some actions
Section titled “Handling only some actions”wrapAsPartialLocalHandler works like wrapAsLocalHandler, but you only implement some of a domain’s actions. This is handy for a client that answers a few calls itself and lets the rest travel on to the server.
const partial = act_user.wrapAsPartialLocalHandler({ getUser: async ({ userId }) => cache.get(userId), // the rest are forwarded over the connection});Once you’ve written a handler, pass it to serveChannel on the listening side. To answer toConnector pushes on the dialing side, pass it to connectChannel’s onPush.
One runtime may have an action-specific executor and a domain fallback; the specific route wins. Two
different local executors at the same route priority are rejected during setup with
duplicate_action_executor, so handler composition cannot silently shadow an implementation.