Error Domains
Declare typed error schemas with defineNiceError and the err() helper.
An error domain is a named group of related errors. Each entry in its schema maps an error id to a message, an HTTP status, and an optional typed context payload.
Core concepts
Section titled “Core concepts”- Domain — a named group of related errors (
err_user_auth,err_payment). - Schema — maps error id strings to message, HTTP status, and optional typed context.
- Error id — identifies what went wrong; multiple ids can be active on one error.
- Context — structured data attached to an id (e.g.
{ username: string }). - Hydration — deserializing context from a JSON round-trip back to the original typed value.
Defining a domain
Section titled “Defining a domain”import { defineNiceError, err } from "@nice-code/error";
export const err_auth = defineNiceError({ domain: "err_auth", schema: { // No context — second arg to fromId is not accepted account_locked: err({ message: "Account is locked", httpStatusCode: 403, }),
// Required context — second arg to fromId is required invalid_credentials: err<{ username: string }>({ message: ({ username }) => `Invalid credentials for: ${username}`, httpStatusCode: 401, context: { required: true }, }),
// Optional context rate_limited: err<{ retryAfter: number }>({ message: (ctx) => (ctx ? `Retry after ${ctx.retryAfter}s` : "Rate limited"), httpStatusCode: 429, context: {}, }), },} as const);The
as conston the definition object is what lets TypeScript infer literal ids and context shapes. Don’t omit it.
The err() helper
Section titled “The err() helper”err() declares one schema entry. Whether context is accepted or required at creation depends on how you call it:
| Schema entry | fromId("id") | fromId("id", ctx) |
|---|---|---|
err() / err({ message, httpStatusCode }) | ✓ | ✗ |
err<C>({ context: {} }) | ✓ optional | ✓ optional |
err<C>({ context: { required: true } }) | ✗ | ✓ required |
message and httpStatusCode can each be a static value or a function of the context.
Custom serialization
Section titled “Custom serialization”For context that isn’t JSON-native (an Error, a Date, a Map), attach a serialization pair so it survives a round trip:
fs_error: err<{ cause: NodeJS.ErrnoException }>({ message: ({ cause }) => `FS error: ${cause.message}`, context: { required: true, serialization: { toJsonSerializable: ({ cause }) => ({ code: cause.code, message: cause.message }), fromJsonSerializable: (obj) => ({ cause: Object.assign(new Error(obj.message), obj) }), }, },}),The serialized form travels on the wire; hydration restores the original typed value on the other side.
Utility types
Section titled “Utility types”import type { InferNiceError, InferNiceErrorHydrated } from "@nice-code/error";
type TAuthError = InferNiceError<typeof err_auth>;type TAuthErrorHydrated = InferNiceErrorHydrated<typeof err_auth>;