@geekmidas/errors
v0.0.1
Published
Type-safe HTTP error classes with full TypeScript support, providing structured error handling for REST APIs and HTTP-based applications.
Downloads
255
Readme
@geekmidas/errors
Type-safe HTTP error classes with full TypeScript support, providing structured error handling for REST APIs and HTTP-based applications.
Features
- Type-Safe Errors: Full TypeScript support with proper error hierarchies
- HTTP Status Codes: Pre-built error classes for all common HTTP errors
- Structured Error Details: Include additional context and debugging information
- Error Factories: Convenient factory functions for creating errors
- Type Guards: Runtime type checking for error instances
- JSON Serialization: Built-in support for error serialization
- Error Wrapping: Wrap unknown errors into HTTP errors
- Cause Chaining: ES2022 error cause support for error chains
Installation
pnpm add @geekmidas/errorsQuick Start
Basic Usage
import {
BadRequestError,
NotFoundError,
UnauthorizedError,
InternalServerError
} from '@geekmidas/errors';
// Throw specific error types
throw new NotFoundError('User not found');
throw new BadRequestError('Invalid email format');
throw new UnauthorizedError('Invalid token');
throw new InternalServerError('Database connection failed');With Error Details
import { BadRequestError, NotFoundError } from '@geekmidas/errors';
// Include additional context
throw new NotFoundError('User not found', { userId: '123' });
throw new BadRequestError('Validation failed', {
field: 'email',
value: 'invalid-email',
message: 'Must be a valid email address'
});Available Error Classes
Client Errors (4xx)
import {
BadRequestError, // 400
UnauthorizedError, // 401
ForbiddenError, // 403
NotFoundError, // 404
MethodNotAllowedError, // 405
ConflictError, // 409
UnprocessableEntityError, // 422
TooManyRequestsError // 429
} from '@geekmidas/errors';
// 400 Bad Request
throw new BadRequestError('Invalid input');
// 401 Unauthorized
throw new UnauthorizedError('Authentication required');
// 403 Forbidden
throw new ForbiddenError('Insufficient permissions', {
required: 'admin',
current: 'user'
});
// 404 Not Found
throw new NotFoundError('Resource not found');
// 405 Method Not Allowed
throw new MethodNotAllowedError('DELETE not supported', ['GET', 'POST', 'PUT']);
// 409 Conflict
throw new ConflictError('Email already exists', {
email: '[email protected]'
});
// 422 Unprocessable Entity
throw new UnprocessableEntityError('Validation failed', {
email: 'Invalid email format',
age: 'Must be 18 or older'
});
// 429 Too Many Requests
throw new TooManyRequestsError('Rate limit exceeded', 60); // retry after 60 secondsServer Errors (5xx)
import {
InternalServerError, // 500
NotImplementedError, // 501
BadGatewayError, // 502
ServiceUnavailableError, // 503
GatewayTimeoutError // 504
} from '@geekmidas/errors';
// 500 Internal Server Error
throw new InternalServerError('Database connection failed');
// 501 Not Implemented
throw new NotImplementedError('Feature not yet implemented');
// 502 Bad Gateway
throw new BadGatewayError('Upstream server error');
// 503 Service Unavailable
throw new ServiceUnavailableError('Maintenance in progress', 300); // retry after 5 minutes
// 504 Gateway Timeout
throw new GatewayTimeoutError('Upstream server timeout');Error Factories
Use convenient factory functions for creating errors:
import { createError } from '@geekmidas/errors';
// Short, descriptive method names
throw createError.badRequest('Invalid input');
throw createError.notFound('User not found');
throw createError.unauthorized('Invalid token');
throw createError.forbidden('Access denied');
throw createError.conflict('Resource already exists');
throw createError.internalServerError('Something went wrong');
// With details
throw createError.notFound('User not found', { userId: '123' });
throw createError.badRequest('Validation failed', {
errors: ['Invalid email', 'Password too short']
});Generic Error Factory
Create errors dynamically with type-safe options:
import { createHttpError } from '@geekmidas/errors';
// TypeScript knows which options are valid for each status code
throw createHttpError(404, 'Not found');
throw createHttpError(429, 'Rate limited', { retryAfter: 60 });
throw createHttpError(422, 'Validation failed', {
validationErrors: {
email: 'Invalid format',
age: 'Must be 18+'
}
});Type Guards
Check error types at runtime:
import {
isHttpError,
isClientError,
isServerError,
NotFoundError
} from '@geekmidas/errors';
try {
// some code
} catch (error) {
if (isHttpError(error)) {
console.log(`HTTP ${error.statusCode}: ${error.message}`);
console.log('Details:', error.details);
}
if (isClientError(error)) {
// 4xx errors - client's fault
console.log('Client error:', error.message);
}
if (isServerError(error)) {
// 5xx errors - server's fault
console.error('Server error:', error.message);
// Alert monitoring system
}
if (error instanceof NotFoundError) {
// Specific error type
console.log('Resource not found');
}
}Error Wrapping
Wrap unknown errors into HTTP errors:
import { wrapError } from '@geekmidas/errors';
try {
await someOperation();
} catch (error) {
// Wrap unknown error as 500
throw wrapError(error);
// Or wrap with specific status and message
throw wrapError(error, 503, 'Service temporarily unavailable');
}Error Serialization
Serialize errors to JSON:
import { NotFoundError } from '@geekmidas/errors';
const error = new NotFoundError('User not found', { userId: '123' });
// Get JSON representation
const json = error.toJSON();
// {
// name: 'NotFoundError',
// message: 'User not found',
// statusCode: 404,
// statusMessage: 'Not Found',
// details: { userId: '123' },
// stack: '...'
// }
// Get error body for HTTP response
const body = error.body;
// JSON string: '{"message":"User not found","details":{"userId":"123"}}'Error Cause Chaining
Chain errors using ES2022 error cause:
import { InternalServerError } from '@geekmidas/errors';
try {
await database.connect();
} catch (originalError) {
throw new InternalServerError('Failed to connect to database', {
cause: originalError,
details: { host: 'localhost', port: 5432 }
});
}Express Middleware
Handle errors in Express applications:
import { isHttpError } from '@geekmidas/errors';
import type { Request, Response, NextFunction } from 'express';
function errorHandler(
error: unknown,
req: Request,
res: Response,
next: NextFunction
) {
if (isHttpError(error)) {
res.status(error.statusCode).json({
error: {
message: error.message,
code: error.code,
details: error.details
}
});
} else {
// Unknown error
console.error('Unexpected error:', error);
res.status(500).json({
error: {
message: 'Internal Server Error'
}
});
}
}
// Use in Express app
app.use(errorHandler);Hono Middleware
Handle errors in Hono applications:
import { Hono } from 'hono';
import { isHttpError } from '@geekmidas/errors';
const app = new Hono();
app.onError((error, c) => {
if (isHttpError(error)) {
return c.json(
{
error: {
message: error.message,
code: error.code,
details: error.details
}
},
error.statusCode
);
}
console.error('Unexpected error:', error);
return c.json(
{ error: { message: 'Internal Server Error' } },
500
);
});Custom Error Classes
Extend base classes for custom errors:
import { HttpError } from '@geekmidas/errors';
export class CustomBusinessError extends HttpError {
constructor(message?: string, details?: any) {
super(400, message, { details, code: 'BUSINESS_RULE_VIOLATION' });
}
}
// Usage
throw new CustomBusinessError('Cannot delete user with active orders', {
userId: '123',
activeOrders: 5
});Error Context
Add context to errors for better debugging:
import { NotFoundError } from '@geekmidas/errors';
function getUserById(id: string) {
const user = database.findUser(id);
if (!user) {
throw new NotFoundError('User not found', {
userId: id,
requestId: req.headers['x-request-id'],
timestamp: new Date().toISOString(),
source: 'getUserById'
});
}
return user;
}HTTP Status Codes
Use the HttpStatusCode enum for type-safe status codes:
import { HttpStatusCode } from '@geekmidas/errors';
// Success
HttpStatusCode.OK // 200
HttpStatusCode.CREATED // 201
HttpStatusCode.NO_CONTENT // 204
// Redirection
HttpStatusCode.MOVED_PERMANENTLY // 301
HttpStatusCode.NOT_MODIFIED // 304
// Client Errors
HttpStatusCode.BAD_REQUEST // 400
HttpStatusCode.UNAUTHORIZED // 401
HttpStatusCode.FORBIDDEN // 403
HttpStatusCode.NOT_FOUND // 404
HttpStatusCode.CONFLICT // 409
HttpStatusCode.UNPROCESSABLE_ENTITY // 422
HttpStatusCode.TOO_MANY_REQUESTS // 429
// Server Errors
HttpStatusCode.INTERNAL_SERVER_ERROR // 500
HttpStatusCode.SERVICE_UNAVAILABLE // 503Best Practices
1. Use Specific Error Classes
// ❌ Don't use generic Error
throw new Error('User not found');
// ✅ Use specific HTTP error
throw new NotFoundError('User not found', { userId: '123' });2. Include Helpful Details
// ❌ Minimal information
throw new BadRequestError('Invalid input');
// ✅ Include context
throw new BadRequestError('Invalid email format', {
field: 'email',
value: userInput,
expectedFormat: '[email protected]'
});3. Use Type Guards
// ❌ Assume error type
catch (error: any) {
console.log(error.statusCode); // Unsafe
}
// ✅ Check error type
catch (error) {
if (isHttpError(error)) {
console.log(error.statusCode); // Safe
}
}4. Chain Errors
// ✅ Preserve original error context
try {
await database.query();
} catch (originalError) {
throw new InternalServerError('Database query failed', {
cause: originalError,
query: 'SELECT * FROM users'
});
}TypeScript Types
import type {
HttpError,
HttpErrorOptions,
HttpErrorConstructor
} from '@geekmidas/errors';
// Options for creating errors
interface HttpErrorOptions {
statusMessage?: string;
details?: any;
code?: string;
cause?: Error;
}
// Constructor type for factory patterns
type HttpErrorConstructor = new (
message?: string,
options?: HttpErrorOptions
) => HttpError;Related Packages
- @geekmidas/constructs - Uses these error classes in endpoints
- @geekmidas/client - Handles these errors on the client side
- @geekmidas/logger - Log errors with structured context
License
MIT
