@ooneex/exception package gives the framework one consistent way to fail. Instead of throwing bare Error objects, you throw an Exception — a subclass of the native Error that also carries an HTTP status code, an optional grouping key, immutable contextual data, the timestamp it was created, and (when you wrap a native error) the original error. Specialized subclasses like NotFoundException preset the status code so the most common HTTP failures read clearly at the throw site and map straight onto an error response.
Why this system
- HTTP status mapping. Every exception carries a
statuscode (default500). Status codes come from@ooneex/http-status, so an exception maps directly onto the response it should produce. - Typed, immutable data. Attach a plain object of contextual
data(the failing field, an entity id) at the throw site. It is frozen withObject.freeze, so the context that explains the failure cannot be mutated as the error propagates. - Built-in timestamp. Each exception records its creation
date, so logs and error responses have an accurate moment of failure without extra bookkeeping. - JSON stack traces.
stackToJson()parses the raw stack string into a structuredExceptionStackFrameType[]— function name, file, line, column — ready for logging or a debug-only response field. - Native error wrapping. Pass a caught
Errorstraight into the constructor; its message is reused and the original is preserved onnative, so you keep the underlying stack and context. - Specialized exceptions.
BadRequestException,NotFoundException,UnauthorizedException, andMethodNotAllowedExceptionpreset the right status code so intent is obvious at the throw site.
How it works
Exception extends Error implements IException. The constructor takes the message (or an Error to wrap) and an options object; it reads status, key, and data, freezes data, sets name to the concrete class name, and — if the message was an Error — stores it on native. The date is captured the moment the instance is created.
| Member | Type | Description |
|---|---|---|
key | string | null | Optional grouping key for the error (null by default). |
date | Date | Timestamp captured when the exception is constructed. |
status | StatusCodeType | HTTP status code; defaults to 500. |
data | Readonly<Record<string, unknown>> | Frozen contextual data attached at the throw site. |
native | Error | undefined | The original error, when a native Error was wrapped. |
message | string | Error message (inherited from Error). |
name | string | The concrete class name, e.g. "NotFoundException". |
stack | string | undefined | Raw stack trace string (inherited from Error). |
stackToJson() | ExceptionStackFrameType[] | null | Parses stack into structured frames, or null when no stack exists. |
stackToJson() matches ExceptionStackFrameType:
The base Exception
Throw anException directly when no specialized type fits. With only a message it defaults to status 500 and empty data:
data object is frozen, so reading it downstream is safe — attempting to mutate it has no effect.
Specialized exceptions
The specialized exceptions extendException and preset the status code from @ooneex/http-status. Their constructor is positional: a message, a required key string, and an optional data object (defaults to {}).
| Exception | Status code | HttpStatus.Code |
|---|---|---|
BadRequestException | 400 | BadRequest |
UnauthorizedException | 401 | Unauthorized |
NotFoundException | 404 | NotFound |
MethodNotAllowedException | 405 | MethodNotAllowed |
Throwing from a service or controller
Throw exceptions where the failure is detected — in a service or a controller — and let the calling layer turn them into a response. Because each exception carries its ownstatus and data, the handler does not need to know how to classify the failure:
status and data you attached at the throw site flow straight into the error response. See Response for how context.response.exception(...) and the related helpers serialize this, and Controller for where this handling belongs.
Wrapping a native error
When a third-party call or a built-in throws a plainError, wrap it instead of swallowing it. Pass the caught error as the message — the constructor reuses its message and keeps the original on native, so the underlying context is never lost:
exception.message and exception.data describe the application-level failure, while exception.native holds the original error and its stack.
Inspecting the stack as JSON
stackToJson() turns the raw stack string into structured frames you can log or filter, rather than a single opaque string:
Best practices
- Throw exceptions, not bare errors. Use
Exception(or a specialized subclass) so every failure carries a status code and structured data that a handler can act on uniformly. - Pick the most specific type. Reach for
NotFoundException,BadRequestException,UnauthorizedException, orMethodNotAllowedExceptionwhen they fit; their preset status codes make intent obvious at the throw site. - Attach context as
data, not in the message. Put ids, field names, and constraints indataso they stay machine-readable; keep themessagehuman-readable. - Use
keyfor grouping. A stablekeylets you classify, route, and translate errors without parsing the message string. - Log with structure. Send exceptions to the Logger and use
stackToJson()anddatafor searchable, structured records rather than concatenated strings. - Guard debug output. Expose
stackToJson()only in non-production responses; in production surfacemessage,status, and safedataonly. - Wrap, don’t discard. When catching a native error, wrap it so its message and stack survive on
nativewhile you add application context.