@mkvlrn/app-error
v0.4.4
Published
Map app error codes to HTTP statuses
Maintainers
Readme
@mkvlrn/app-error
Type-safe HTTP status utilities and application errors.
@mkvlrn/app-error provides two small, related APIs:
- HTTP status conversions between numeric codes, status names, and reason phrases.
- A single application-wide error definition that associates application error codes with HTTP statuses.
TOC
Installation
[!NOTE] This package is hosted both at npm and jsr, and is ESM only.
| Package manager | npm | JSR |
| --------------- | -------------------------------- | ------------------------------------------ |
| Bun | bun add @mkvlrn/app-error | bunx jsr add @mkvlrn/[email protected] |
| npm | npm install @mkvlrn/app-error | npx jsr add @mkvlrn/[email protected] |
| pnpm | pnpm add @mkvlrn/app-error | pnpm dlx jsr add @mkvlrn/[email protected] |
| Yarn | yarn add @mkvlrn/app-error | yarn dlx jsr add @mkvlrn/[email protected] |
| Deno | deno add npm:@mkvlrn/app-error | deno add jsr:@mkvlrn/[email protected] |
HTTP status
The httpStatus export provides type-safe conversions between HTTP status codes, names, and reason phrases.
import { httpStatus } from "@mkvlrn/app-error";
httpStatus.codeFromName("NotFound");
// 404
httpStatus.codeFromPhrase("Not Found");
// 404
httpStatus.nameFromCode(404);
// "NotFound"
httpStatus.nameFromPhrase("Not Found");
// "NotFound"
httpStatus.phraseFromCode(404);
// "Not Found"
httpStatus.phraseFromName("NotFound");
// "Not Found"The package also exports the corresponding types:
import type { StatusCode, StatusName, StatusPhrase } from "@mkvlrn/app-error";
const code: StatusCode = 404;
const name: StatusName = "NotFound";
const phrase: StatusPhrase = "Not Found";httpStatus can be used independently of AppError.
Application errors
AppError is designed around a single application-wide error definition.
Instead of creating a separate class or type for every possible application error, define the application's complete error vocabulary in one place. Each error code is mapped to an HTTP status name.
import { AppError } from "@mkvlrn/app-error";
export const appErrors = AppError.define({
resourceNotFound: "NotFound",
externalApiError: "BadGateway",
internalApiError: "InternalServerError",
});Add new application errors to this mapping as the application grows. The resulting factory provides type-safe creation, throwing, and inspection of all errors defined by the application.
Create errors
const error = appErrors.create("resourceNotFound", "The requested resource does not exist");
error.errorCode;
// "resourceNotFound"
error.statusCode;
// 404
error.statusName;
// "NotFound"
error.statusPhrase;
// "Not Found"The error code is inferred from the keys of the mapping, so invalid codes are rejected by TypeScript:
appErrors.create("somethingElse", "This error code does not exist");
// Type errorThrow errors
The same factory can throw an error directly:
appErrors.throw("resourceNotFound", "The requested resource does not exist");throw() returns never, so it can be used naturally in functions that otherwise return a value.
Attach a cause
An underlying error or additional failure context can be attached with cause:
try {
await fetchSomething();
} catch (cause) {
appErrors.throw("externalApiError", "The external service failed", cause);
}The cause is available through the standard Error.cause property and is included as details when the error is serialized.
Check errors
The factory provides a type guard for checking unknown values:
try {
// ...
} catch (error) {
if (appErrors.is(error)) {
error.errorCode;
error.statusCode;
error.statusName;
error.statusPhrase;
}
}The guard narrows the value to the application's complete AppError type.
For the example above, that type is:
AppError<"resourceNotFound" | "externalApiError" | "internalApiError">;Infer the application error type
If the aggregate application error type is needed elsewhere, it can be inferred directly from the factory:
export type AppErrorType = ReturnType<typeof appErrors.create>;This produces:
AppError<"resourceNotFound" | "externalApiError" | "internalApiError">;There is no need to declare or maintain separate types for individual errors.
Serialize errors
AppError.serialize() converts an error into a plain object suitable for HTTP responses, logging, or other serialization:
const error = appErrors.create("resourceNotFound", "The requested resource does not exist");
error.serialize();The result is:
{
errorCode: "resourceNotFound",
statusCode: 404,
statusName: "NotFound",
statusPhrase: "Not Found",
message: "The requested resource does not exist",
details: undefined,
}For example, an HTTP handler can use the error's status directly:
if (appErrors.is(error)) {
res.status(error.statusCode).json(error.serialize());
}API
AppError<T>
An Error subclass containing:
errorCode— application-specific error code.statusCode— numeric HTTP status code.statusName— HTTP status name.statusPhrase— HTTP reason phrase.message— human-readable error message.cause— optional underlying error or failure context.
T is the union of application error codes defined by AppError.define().
The constructor is protected; errors should be created through a factory.
AppError.define(mapping)
Creates an application error factory from an error-to-status mapping.
const appErrors = AppError.define({
resourceNotFound: "NotFound",
invalidInput: "BadRequest",
});The mapping keys become the application's error codes, while the values are StatusName values.
The returned factory provides:
create(code, message, cause?)throw(code, message, cause?)is(value)
httpStatus
Provides conversions between:
StatusCodeStatusNameStatusPhrase
All supported HTTP statuses are represented by the exported types.
StatusCode
Union of all supported numeric HTTP status codes.
const code: StatusCode = 404;StatusName
Union of all supported HTTP status names.
const name: StatusName = "NotFound";StatusPhrase
Union of all supported HTTP reason phrases.
const phrase: StatusPhrase = "Not Found";License
MIT
