Message Rendering
Render error messages from validated fields in @deessejs/errors.
When you attach a fields schema to an error factory, the message is computed from the parsed data, not the raw input. This lets you format fields that have been transformed, validated, or branded by your schema.
Function form (recommended)
Pass a function that takes the parsed fields and returns the message. The data you receive is the Standard Schema output, with all transforms already applied:
import { z } from "zod";
import { error } from "@deessejs/errors";
const ValidationError = error({
name: "ValidationError",
fields: z.object({
field: z.string(),
reason: z.string(),
}),
message: (data) => `Field "${data.field}" is invalid: ${data.reason}`,
});
const err = ValidationError({ field: "email", reason: "invalid format" });
console.log(err.message); // -> Field "email" is invalid: invalid formatThe function receives the Standard Schema output type. After a z.coerce.number(), z.brand(), or any transform, you see the final shape.
With Valibot
import * as v from "valibot";
import { error } from "@deessejs/errors";
const FormatError = error({
name: "FormatError",
fields: v.object({
expected: v.string(),
actual: v.string(),
}),
message: (data) => `Expected ${data.expected}, got ${data.actual}`,
});
const err = FormatError({ expected: "number", actual: "string" });
console.log(err.message); // -> Expected number, got stringWith ArkType
import { type } from "@ark/type";
import { error } from "@deessejs/errors";
const ApiError = error({
name: "ApiError",
fields: type({
method: "string",
endpoint: "string",
statusCode: "number",
}),
message: (data) => `${data.method.toUpperCase()} ${data.endpoint} failed with status ${data.statusCode}`,
});
const err = ApiError({ method: "post", endpoint: "/users", statusCode: 500 });
console.log(err.message); // -> POST /users failed with status 500Modifiers via the function body
The function form replaces the legacy :upper / :lower / :json modifiers. Implement them inline with the JS string APIs you already know:
import { z } from "zod";
import { error } from "@deessejs/errors";
const UserCreatedError = error({
name: "UserCreatedError",
fields: z.object({ userId: z.string() }),
message: (data) => `Created user: ${data.userId.toUpperCase()}`,
});
const err = UserCreatedError({ userId: "usr_abc" });
console.log(err.message); // -> Created user: USR_ABCJSON.stringify replaces :json:
import { z } from "zod";
import { error } from "@deessejs/errors";
const DataError = error({
name: "DataError",
fields: z.object({ payload: z.object({ id: z.number(), name: z.string() }) }),
message: (data) => `Invalid data: ${JSON.stringify(data.payload)}`,
});
const err = DataError({ payload: { id: 1, name: "Ada" } });
console.log(err.message); // -> Invalid data: {"id":1,"name":"Ada"}Deprecated: string template (removed in 2.0.0)
The string-template form (message: "Field {field} is invalid") was deprecated in 1.4.0 and removed in 2.0.0 for the schema overload. A factory declared with a string message is no longer accepted alongside a schema; the schema overload requires a function-form message. The legacy no-schema form (no fields schema) still accepts a string message, and renders it without placeholder substitution:
import { error } from "@deessejs/errors";
const ValidationError = error({
name: "ValidationError",
message: "Field {field} is invalid",
});
const err = ValidationError();
console.log(err.message); // -> Field {field} is invalidBehavior summary:
- The function form is the only path that produces type-checked, post-transform fields. New code should use the function form.
- For no-schema factories, a string
messageis rendered verbatim. The function form is the recommended path. - Schema validation, when supplied, runs before rendering; failed validation throws
ArgsValidationError.