Skip to content

Serialization & Transport

Send typed errors across HTTP and reconstruct them with full types.

A NiceError serializes to plain JSON and reconstructs on the other side — with its ids, context, and HTTP status intact.

const json = error.toJsonObject(); // plain JSON object — safe over HTTP
const str = error.toJsonString(); // JSON string
const response = error.toHttpResponse(); // a Response with the correct HTTP status

On the receiving side, castNiceError turns any caught value into a NiceError:

import { castNiceError } from "@nice-code/error";
const caught = castNiceError(unknownValue); // always returns a NiceError
if (err_auth.isExact(caught)) {
const hydrated = err_auth.hydrate(caught); // deserialize context
const { username } = hydrated.getContext("invalid_credentials");
}

castNiceError handles NiceError instances, serialized JSON objects, native Errors, error-like objects, nullish values, and primitives — it never throws.

When you have a specific domain in mind, castAndHydrate casts, checks the domain, and hydrates in a single call:

import { castAndHydrate, matchFirst } from "@nice-code/error";
const error = castAndHydrate(unknownValue, err_auth);
// → a hydrated NiceError when it belongs to err_auth, otherwise the raw cast NiceError
if (err_auth.isExact(error)) {
const message = matchFirst(error, {
invalid_credentials: ({ username }) => `Wrong password for ${username}`,
account_locked: () => "Account locked",
});
}

The wire only carries JSON. If an id’s context held a Date, an Error, or anything declared with a custom serialization pair, hydration is what turns the JSON-safe form back into the real typed value. Always hydrate (or castAndHydrate) before reading context that came off a boundary.

// Server
return error.toHttpResponse();
// Client
const caught = castNiceError(await res.json());
if (err_auth.isExact(caught)) {
const hydrated = err_auth.hydrate(caught);
// hydrated.getContext(...) is now fully typed and real
}

Next: Handling errors →