@maroonedsoftware/errors
v1.6.0
Published
A comprehensive error handling library for HTTP APIs with built-in support for PostgreSQL error mapping and class-level error decorators.
Maintainers
Readme
@maroonedsoftware/errors
A comprehensive error handling library for HTTP APIs with built-in support for PostgreSQL error mapping and class-level error decorators.
Installation
pnpm add @maroonedsoftware/errorsFeatures
- ServerkitError — Base error class with
details,cause,internalDetails, and chainable setters - HttpError —
ServerkitErrorsubclass that adds an HTTP status code and response headers - OnError — Class decorator for automatic error handling on all methods
- PostgresErrorHandler — Maps PostgreSQL error codes to appropriate HTTP errors
- Type-safe — Full TypeScript support with inferred status messages
Usage
ServerkitError
The base class for all ServerKit-aware errors. Use it directly when an error isn't HTTP-shaped (e.g. a domain rule violation in a worker), or extend it to build your own typed error hierarchy. The errorMiddleware in @maroonedsoftware/koa recognises ServerkitError and renders its details in the 500 response body — bare Error instances get a generic "Internal Server Error" with no details.
import { ServerkitError } from '@maroonedsoftware/errors';
throw new ServerkitError('Quota exceeded')
.withDetails({ resource: 'invoices', limit: 100 })
.withInternalDetails({ accountId: 'acct_42' });To build your own:
class DomainError extends ServerkitError {}
throw new DomainError('Pricing rule violated').withDetails({ rule: 'min-margin' });HttpError
Create HTTP errors with fluent method chaining:
import { HttpError, httpError, unauthorizedError } from '@maroonedsoftware/errors';
// Using the factory function
throw httpError(404);
// With the default status message (must match the status code's mapped message)
throw httpError(400, 'Bad Request');
// With error details
throw httpError(400).withDetails({
email: 'Invalid email format',
password: 'Must be at least 8 characters',
});
// With response headers
throw httpError(401).withHeaders({
'WWW-Authenticate': 'Bearer realm="api"',
});
// Shorthand for unauthorized with WWW-Authenticate header
throw unauthorizedError('Bearer realm="api"');
// With error chaining
throw httpError(500).withCause(originalError);
// With internal details (for logging, not exposed to clients)
throw httpError(500).withInternalDetails({
userId: 123,
requestId: 'abc-123',
});
// Combine multiple options
throw httpError(409).withDetails({ username: 'Already taken' }).withCause(dbError).withInternalDetails({ attemptedUsername: 'john_doe' });Type Guards
Check if an error is an HttpError (subclass) or any ServerkitError:
import { IsHttpError, IsServerkitError } from '@maroonedsoftware/errors';
try {
await someOperation();
} catch (error) {
if (IsHttpError(error)) {
console.log(error.statusCode); // typed
console.log(error.details);
} else if (IsServerkitError(error)) {
// Any non-HTTP ServerkitError — still has details/cause/internalDetails.
console.log(error.details);
}
}IsServerkitError is true for HttpError, KmsError (from @maroonedsoftware/encryption), and any subclass you define.
OnError Decorator
Automatically wrap all class methods with error handling:
import { OnError, httpError } from '@maroonedsoftware/errors';
@OnError(error => {
console.error('Error caught:', error);
throw httpError(500).withCause(error);
})
class MyService {
async doSomething() {
// If this throws, it will be caught and handled
throw new Error('Something went wrong');
}
get computedValue() {
// Getters are also wrapped
throw new Error('Getter failed');
}
}PostgreSQL Error Handling
Convert PostgreSQL errors to appropriate HTTP errors:
import { PostgresErrorHandler, OnPostgresError } from '@maroonedsoftware/errors';
// Manual usage
try {
await db.insert(users).values({ email: '[email protected]' });
} catch (error) {
PostgresErrorHandler(error);
// 23505 (unique violation) → 409 Conflict
// 23503 (foreign key violation) → 404 Not Found
// 22000, 22003, 22004, 22023, 23502, 22P02, 23514 (validation) → 400 Bad Request
// 40000, 40001, 40002 (transaction rollback) → 500 Internal Server Error
// 40P01 (deadlock) → 500 Internal Server Error
// Unknown PostgreSQL codes → 500 Internal Server Error
// Non-PostgreSQL errors are re-thrown as-is
}
// Using the decorator (recommended)
@OnPostgresError()
class UserRepository {
async create(data: UserData) {
return await db.insert(users).values(data);
}
async findById(id: number) {
return await db.select().from(users).where(eq(users.id, id));
}
}Supported HTTP Status Codes
All standard 4xx and 5xx status codes are supported with their default messages:
| Code | Message | | ---- | ----------------------------------------- | | 400 | Bad Request | | 401 | Unauthorized | | 403 | Forbidden | | 404 | Not Found | | 409 | Conflict | | 422 | Unprocessable Entity | | 429 | Too Many Requests | | 500 | Internal Server Error | | 502 | Bad Gateway | | 503 | Service Unavailable | | ... | and more |
API Reference
ServerkitError
Base class for all ServerKit errors.
| Property | Type | Description |
| ----------------- | ------------------------- | -------------------------------------------------------- |
| message | string | Error message |
| details | Record<string, unknown> | Response-shaped details (rendered by errorMiddleware) |
| cause | Error | Underlying error for chaining |
| internalDetails | Record<string, unknown> | Internal debugging info (never rendered to the response) |
Methods (all return the instance for chaining):
| Method | Description |
| ------------------------------ | -------------------------------------------- |
| withDetails(details) | Set response-shaped details |
| withCause(error) | Set the underlying cause |
| withInternalDetails(details) | Set internal debug info (not exposed to API) |
IsServerkitError(value) — type guard. Returns true for ServerkitError and any subclass (including HttpError and KmsError).
HttpError
Extends ServerkitError. Inherits all of the above and adds:
| Property | Type | Description |
| ------------- | ------------------------ | ----------------------------------- |
| statusCode | HttpStatusCodes | The HTTP status code |
| headers | Record<string, string> | HTTP headers to include in response |
| Method | Description |
| ------------------------------- | ------------------------------------------- |
| withHeaders(headers) | Replace the headers map |
| addHeader(key, value) | Set or overwrite a single header (chainable)|
IsHttpError(value) — type guard for HttpError instances specifically.
License
MIT
