GIVE ERRORS
A NAME.
@domain-first/errors
An error is usually a message string and an unknown in the catch block. Errors gives each one a namespaced code and typed details — recognizable in your process and on the other side of the wire.
Something went wrong.
But what?
In most TypeScript codebases errors are new Error('something went wrong'). That works until someone needs to react to one specific failure.
The catch block gets unknown. To tell one failure from another, code compares message strings or guesses at properties.
Useful data is glued into the message. The project id sits inside Project 42 not found: readable for people, lost for code.
Proper error classes cost boilerplate. A name, a code, a typed payload, serialization — so most projects write one or two and stop.
Identity dies at the boundary. After JSON, a queue or an HTTP response, instanceof is gone. The client compares hand-typed codes that drift from the server.
Errors are domain vocabulary.
A failure is a named part of the domain, just like an entity. Errors groups failures by namespace and gives each one a code and a typed payload. Recognition works in two modes: by class inside the process, by code across boundaries.
A matching code tells you which error arrived. Validate transported details before using them.
Say what can go wrong.
That’s the code.
First the idea, in the words you would use with a teammate. Then the code, right next to it. Read them side by side: the code you write is the idea, almost word for word.
01“Things can go wrong with projects. Let’s keep those failures together.”
const ProjectErrors = errorNamespace('PROJECT');02“A project can be not found, and then we need to know which one.”
const ProjectNotFound = ProjectErrors.define<{
projectId: string;
}>('NOT_FOUND');03“It can also be archived.”
const ProjectArchived = ProjectErrors.define('ARCHIVED');04“When it’s not found, the server answers 404.”
if (ProjectNotFound.is(error)) {
return Response.json(error.serialized, { status: 404 });
}05“And the client recognizes it as that same failure. Or as any project failure.”
if (ProjectNotFound.matchesCode(body)) showNotFound();
if (ProjectErrors.matchesCode(body)) showProjectError();5 sentences. 5 pieces of code. Nothing in the code that the idea didn’t ask for. No error class boilerplate, no prototype fixes, no hand-typed codes. Imports are omitted.
Name the first failure.
npm i @domain-first/errorsZero runtime dependencies, ESM and CommonJS. Subnamespaces, metadata and serialization details are in the specification.
API specification ↗Every method and what travels over the wire.
README on npm ↗Guide with more examples.
Handlers REST ↗Map error classes to HTTP statuses once.