@ticatec/node-exception
v1.1.0
Published
Production-ready Express error handling middleware with standardized HTTP error types, centralized exception management, and automatic content negotiation for REST APIs. Type-safe, zero-dependency core.
Keywords
Readme
@ticatec/node-exception
中文 | English
Production-ready Express error handling middleware with standardized HTTP error types, centralized exception management, and automatic content negotiation for REST APIs. Type-safe, zero-dependency core.
🚀 Features
- 🎯 Standardized Error Types: Eight predefined error classes covering common HTTP scenarios
- 🔧 Express Middleware: Drop-in error handling middleware with zero configuration
- 📋 Consistent Response Format: Uniform error responses with comprehensive request context
- 🎨 Content Negotiation: Automatic response formatting (JSON, HTML, plain text)
- 🔍 Development Support: Stack trace inclusion in development environments
- 📘 TypeScript First: Full TypeScript support with complete type definitions
- 📊 Logging Integration: Built-in log4js integration for comprehensive error tracking
- 🌐 IP Detection: Automatic client and server IP address detection
- ⚡ Zero Dependencies: Minimal runtime footprint with only essential dependencies
- ✨ Type-Safe: Improved type safety with null checks and strict typing
- 🛡️ Null-Safe: Automatic default values for optional properties
📦 Installation
npm install @ticatec/node-exceptionPeer Dependencies
npm install log4js🔧 Quick Start
import express from 'express';
import { handleError, AppError, UnauthenticatedError } from '@ticatec/node-exception';
const app = express();
// Your API routes
app.get('/api/protected', (req, res, next) => {
try {
if (!req.headers.authorization) {
throw new UnauthenticatedError();
}
res.json({ message: 'Success!' });
} catch (error) {
next(error);
}
});
// Error handling middleware (must be the last middleware)
app.use((err, req, res, next) => {
handleError(err, req, res);
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});📚 Error Types & Usage
🔑 AppError
Generic application error with custom error codes for business logic errors.
// Database errors
throw new AppError(1001, "Database connection failed");
// Business logic errors
throw new AppError(2001, "Insufficient account balance");
// Validation errors
throw new AppError(3001, "User email already exists");🚫 UnauthenticatedError
For authentication failures (HTTP 401).
// Missing authentication
if (!req.headers.authorization) {
throw new UnauthenticatedError();
}
// Invalid token
if (!isValidToken(token)) {
throw new UnauthenticatedError();
}🔒 InsufficientPermissionError
For authorization failures (HTTP 403).
// Role-based access control
if (!user.hasRole('admin')) {
throw new InsufficientPermissionError();
}
// Resource ownership
if (resource.ownerId !== user.id) {
throw new InsufficientPermissionError();
}❌ IllegalParameterError
For input validation failures (HTTP 400).
// Email validation
if (!isValidEmail(email)) {
throw new IllegalParameterError("Invalid email format");
}
// Required field validation
if (!userId) {
throw new IllegalParameterError("User ID is required");
}
// Range validation
if (age < 0 || age > 150) {
throw new IllegalParameterError("Age must be between 0 and 150");
}🔍 ActionNotFoundError
For non-existent routes or resources (HTTP 404).
// Route not found
if (!routeExists(req.path)) {
throw new ActionNotFoundError();
}
// Resource not found
if (!user) {
throw new ActionNotFoundError();
}⏱️ TimeoutError
For request timeouts (HTTP 408).
// Database query timeout
if (Date.now() - startTime > TIMEOUT_LIMIT) {
throw new TimeoutError();
}
// API request timeout
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new TimeoutError()), 5000);
});🌐 ProxyError
For proxy server issues (HTTP 502).
// Proxy server failure
if (!proxyResponse.ok) {
throw new ProxyError();
}
// Gateway error
if (!upstreamServer.isHealthy()) {
throw new ProxyError();
}🚫 ServiceUnavailableError
For service unavailability (HTTP 503).
// Service maintenance
if (isMaintenanceMode()) {
throw new ServiceUnavailableError();
}
// Server overload
if (activeConnections > maxConnections) {
throw new ServiceUnavailableError();
}🎨 Response Format & Content Negotiation
The library automatically handles content negotiation based on the Accept header:
JSON Response (Default)
{
"code": -1,
"client": "192.168.1.50",
"path": "/api/users",
"method": "GET",
"timestamp": 1699123456789,
"message": "Unauthenticated user is accessing the system.",
"stack": "Error: ... (development only)"
}HTML Response
When Accept: text/html is requested, a professionally styled HTML5 page is returned:
<!DOCTYPE html>
<html>
<head>
<title>Error 401</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.error-container { border: 1px solid #ccc; padding: 20px; border-radius: 5px; }
h1 { color: #d32f2f; }
.stack { background: #f5f5f5; padding: 10px; font-family: monospace; }
</style>
</head>
<body>
<div class="error-container">
<h1>Error Code: 401</h1>
<div><strong>Client:</strong> 192.168.1.50</div>
<div><strong>Method:</strong> GET</div>
<div><strong>Path:</strong> /api/users</div>
<div><strong>Timestamp:</strong> 1699123456789</div>
<div><strong>Message:</strong> Unauthenticated user is accessing the system.</div>
</div>
</body>
</html>Plain Text Response
When Accept: text/plain is requested:
Code: 401
Client: 192.168.1.50
Method: GET
Path: /api/users
Timestamp: 1699123456789
Message: Unauthenticated user is accessing the system.
---Stack Trace---
Error: UnauthenticatedError...📊 Response Fields
| Field | Type | Description |
|-------|------|-------------|
| code | number | Application-specific error code (-1 for generic errors) |
| client | string | Client IP address (defaults to 'unknown' if unavailable) |
| path | string | Full request path (baseUrl + path) |
| method | string | HTTP method (GET, POST, PUT, DELETE, etc.) |
| timestamp | number | Unix timestamp in milliseconds |
| message | string | null | Human-readable error message |
| stack | string | Stack trace (development environments only) |
🔍 Development vs Production
Stack traces are automatically included in development environments:
// Enable development mode by setting request header
fetch('/api/users', {
headers: {
'env': 'development' // or 'dev'
}
});🚦 HTTP Status Code Mapping
| Error Type | HTTP Status | Use Case |
|------------|-------------|----------|
| UnauthenticatedError | 401 Unauthorized | Missing or invalid authentication |
| InsufficientPermissionError | 403 Forbidden | Insufficient permissions/authorization |
| IllegalParameterError | 400 Bad Request | Invalid input parameters or validation |
| ActionNotFoundError | 404 Not Found | Non-existent routes or resources |
| TimeoutError | 408 Request Timeout | Network requests or operations timeout |
| AppError | 500 Internal Server Error | Business logic or application errors |
| ProxyError | 502 Bad Gateway | Proxy server or gateway issues |
| ServiceUnavailableError | 503 Service Unavailable | Service maintenance or overload |
| Other errors | 500 Internal Server Error | Unexpected system errors |
💡 Advanced Usage
Custom HTTP Container
Create custom HTTP adapters for different frameworks:
import { setHttpContainer, HttpContainer } from '@ticatec/node-exception';
class CustomContainer implements HttpContainer {
getRemoteIp(req) {
return req.connection.remoteAddress;
}
getPath(req) {
return req.originalUrl;
}
isDevelopment(req) {
return process.env.NODE_ENV === 'development';
}
sendError(req, res, statusCode, data) {
res.status(statusCode).json(data);
}
}
setHttpContainer(new CustomContainer());Error Code Conventions
Organize your error codes systematically:
// Authentication errors: 1000-1999
const AUTH_ERRORS = {
INVALID_TOKEN: 1001,
EXPIRED_TOKEN: 1002,
MISSING_TOKEN: 1003
};
// Validation errors: 2000-2999
const VALIDATION_ERRORS = {
INVALID_EMAIL: 2001,
WEAK_PASSWORD: 2002,
REQUIRED_FIELD: 2003
};
// Business logic errors: 3000-3999
const BUSINESS_ERRORS = {
INSUFFICIENT_BALANCE: 3001,
DUPLICATE_EMAIL: 3002,
LIMIT_EXCEEDED: 3003
};
throw new AppError(AUTH_ERRORS.INVALID_TOKEN, "Authentication token is invalid");🔧 TypeScript Integration
Full TypeScript support with comprehensive type definitions:
import {
AppError,
UnauthenticatedError,
InsufficientPermissionError,
IllegalParameterError,
ActionNotFoundError,
handleError
} from '@ticatec/node-exception';
import { Request, Response, NextFunction } from 'express';
// Type-safe error middleware
const errorHandler = (
err: AppError | Error,
req: Request,
res: Response,
next: NextFunction
): void => {
handleError(err, req, res);
};
// Custom error with proper typing
class CustomValidationError extends IllegalParameterError {
constructor(field: string, value: any) {
super(`Invalid ${field}: ${value}`);
}
}🏗️ Architecture
The library uses a modular architecture with clear separation of concerns:
- Error Classes (
HttpError.ts): Standardized error types with stack trace support - Error Handler (
handleError.ts): Central error processing and response logic - HTTP Container (
HttpContainer.ts): Framework-agnostic HTTP abstraction - Response Types (
ErrorResponse.ts): Standardized response structure - Utilities (
utils.ts): Response formatting helpers with XSS protection
📋 Requirements
- Node.js: ≥14.0.0
- npm: ≥6.0.0
- log4js: ^6.7.0 (peer dependency)
🆕 Recent Improvements
Version 2.0.0
- ✅ Enhanced type safety with strict TypeScript typing
- ✅ Fixed bug in text response format (Client IP display)
- ✅ Improved HTML error pages with professional styling
- ✅ Added null-safety with default values
- ✅ Removed @ts-ignore and improved error handling
- ✅ Fixed grammar and spelling in error messages
- ✅ Better code documentation with JSDoc comments
🤝 Contributing
We welcome contributions! Please see our Contributing Guidelines for details.
Development Setup
git clone https://github.com/ticatec/node-exception.git
cd node-exception
npm install
npm run dev # Start development mode
npm run build # Build the library
npm run typecheck # Type checking📄 License
MIT © Henry Feng
🔗 Links
- GitHub Repository: https://github.com/ticatec/node-exception
- npm Package: https://www.npmjs.com/package/@ticatec/node-exception
- Issues: https://github.com/ticatec/node-exception/issues
- Documentation: https://docs.ticatec.com/node-exception
