Defining Actions
Root domains, child domains, action schemas, serialization, and thrown errors.
Root and child domains
Section titled “Root and child domains”Start with a root domain — a namespace anchor with no actions — then hang child domains with actions off it. Both ends import these from shared code.
import { createActionRootDomain, actionSchema } from "@nice-code/action";import * as v from "valibot";
export const appRoot = createActionRootDomain({ domain: "app_root" });
export const userDomain = appRoot.createChildDomain({ domain: "user", actions: { getUser: actionSchema() .input({ schema: v.object({ userId: v.string() }) }) .output({ schema: v.object({ id: v.string(), name: v.string() }) }) .throws(err_user, ["not_found"]),
updateName: actionSchema() .input({ schema: v.object({ userId: v.string(), name: v.string() }) }) .output({ schema: v.object({ success: v.boolean() }) }), },});Schemas accept any Standard Schema library — Valibot, Zod, etc.
Serialization for non-JSON types
Section titled “Serialization for non-JSON types”When input or output contains values that aren’t JSON-native (a Date, a Map), pass a serialize/deserialize pair as the 2nd and 3rd arguments:
createAt: actionSchema() .output( { schema: v.object({ createdAt: v.date() }) }, ({ createdAt }) => ({ createdAt: createdAt.toISOString() }), // serialize ({ createdAt }) => ({ createdAt: new Date(createdAt) }), // deserialize ),The handler always receives — and the caller always gets back — the real typed value, never the wire form.
Declaring thrown errors
Section titled “Declaring thrown errors”Attach @nice-code/error domains with .throws(). These surface as typed, narrowable errors at the call site.
import { defineNiceError, err } from "@nice-code/error";
const err_user = defineNiceError({ domain: "err_user", schema: { not_found: err<{ userId: string }>({ message: ({ userId }) => `User not found: ${userId}`, httpStatusCode: 404, context: { required: true }, }), },} as const);
actionSchema() .throws(err_user) // any id from err_user .throws(err_user, ["not_found"]); // only specific idsNext: Channels →