@domain-first/errors
v4.2.2
Published
Strongly typed namespace-based domain errors that remain identifiable across application boundaries
Maintainers
Readme
Define a domain error once, attach typed details, and recognize its code across API responses, queues, and services. No custom error class boilerplate. Zero runtime dependencies.
- Typed details — autocomplete when creating errors and after narrowing with
.is(). - Namespaced codes — organize errors by domain, like
USER.AUTH.INCORRECT_PASSWORD. - Native errors —
instanceof, stack traces, andcausework as expected. - Ready for transport — serialize to a plain object and recognize it with
.matchesCode().
Try it
npm i @domain-first/errorsimport { errorNamespace } from "@domain-first/errors";
const OrderErrors = errorNamespace("ORDER");
const OutOfStock = OrderErrors.define<{ productId: string }>(
"OUT_OF_STOCK",
);
try {
throw new OutOfStock({ productId: "coffee-beans" });
} catch (error: unknown) {
if (OutOfStock.is(error)) {
// string, fully typed
console.log(error.details.productId);
// "ORDER.OUT_OF_STOCK"
console.log(error.code);
} else {
throw error;
}
}Across application boundaries
JSON loses class identity. The error code survives:
const error = new OutOfStock({ productId: "coffee-beans" });
const received: unknown = JSON.parse(
JSON.stringify(error.serialized),
);
if (OutOfStock.matchesCode(received)) {
console.log("Offer a restock notification");
}
OrderErrors.matchesCode(received); // true for any ORDER.* error.is() narrows runtime instances. .matchesCode() checks a code string or an object's code; it does not validate the payload or narrow its details. On an error class, it returns the class's metadata on a match, or undefined otherwise.
.serialized includes code, name, message, details, and metadata. Nested details become flat, dot-separated keys; stack and cause are omitted.
A little more when you need it
| Need | Use |
| ----------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| Nested namespaces | OrderErrors.subnamespace("PAYMENT") → ORDER.PAYMENT.* |
| Static metadata | OrderErrors.defineWithMetadata("OUT_OF_STOCK", { retryable: false }) |
| Custom messages | OrderErrors.define<{ productId: string }>("OUT_OF_STOCK", { message: ({ details }) => details.productId + " is sold out" }) |
| Original cause | new OutOfStock({ productId: "coffee-beans" }, { cause: originalError }) |
ESM and CommonJS supported. MIT licensed.
