Skip to content

Handling Errors

Route errors to handlers with a list of cases or a reusable class.

There are two ways to handle a caught error: a list of cases written inline, or a reusable handler class.

Build a list of cases with forId, forIds, and forDomain. handleWithSync runs the first matching case and returns whether anything handled it.

import { forDomain, forId, forIds } from "@nice-code/error";
const handled = error.handleWithSync([
forId(err_auth, "invalid_credentials", (h) => {
const { username } = h.getContext("invalid_credentials");
return res.status(401).json({ error: `Bad credentials for ${username}` });
}),
forIds(err_auth, ["account_locked", "rate_limited"], (h) => {
return res.status(h.httpStatusCode).json({ error: h.message });
}),
forDomain(err_payment, (h) => {
return res.status(500).json({ error: "Payment failed" });
}),
]);

The handler argument (h) is narrowed to the matched id(s), so getContext is typed inside each case.

await error.handleWithAsync([
forDomain(err_payment, async (h) => {
await db.logFailure(h.message);
await notify(h.toJsonObject());
}),
]);

NiceErrorHandler is a handler you configure once and apply to many errors. It suits a central error boundary.

import { NiceErrorHandler } from "@nice-code/error";
const handler = new NiceErrorHandler()
.forId(err_auth, "invalid_credentials", (h) => "unauthorized")
.forDomain(err_payment, (h) => "payment_error")
.setDefaultHandler((e) => "unknown_error");
handler.handleErrorWithPromiseInspection(error);
  • Functional — local, one-off routing inside a single catch block.
  • Class-based — a shared policy you register once, such as server middleware or a global boundary.