trapx
v1.1.0
Published
A plug-and-play middleware for standardized error handling in TypeScript-based Express.js applications
Maintainers
Readme
TrapX
🎯 Type-safe error handling for TypeScript backends
Features
- 🛡️ Type-safe error handling
- 🎨 Customizable error responses
- 🔄 Framework agnostic core
- 📦 Express.js integration (more coming soon!)
- 🎭 Development/Production modes
- 🎯 Stack trace control
- 🎪 Custom error transformation
Installation
npm install trapx
# or
yarn add trapxQuick Start
import express from 'express';
import { createExpressErrorHandler, NotFoundError } from 'trapx';
const app = express();
app.get('/users/:id', (req, res) => {
throw new NotFoundError('User not found');
});
app.use(createExpressErrorHandler());Real-World Examples
1. API Input Validation
import { ValidationError } from 'trapx';
import { z } from 'zod';
const userSchema = z.object({
email: z.string().email(),
age: z.number().min(18)
});
app.post('/users', (req, res) => {
const result = userSchema.safeParse(req.body);
if (!result.success) {
throw new ValidationError('Invalid user data', {
details: result.error.flatten()
});
}
// Process valid user...
});2. Authentication & Authorization
import { UnauthorizedError, ForbiddenError } from 'trapx';
const authMiddleware = (req, res, next) => {
const token = req.headers.authorization;
if (!token) {
throw new UnauthorizedError('No token provided');
}
try {
const user = verifyToken(token);
req.user = user;
next();
} catch (error) {
throw new UnauthorizedError('Invalid token');
}
};
const adminOnly = (req, res, next) => {
if (req.user.role !== 'admin') {
throw new ForbiddenError('Admin access required', {
details: {
requiredRole: 'admin',
userRole: req.user.role
}
});
}
next();
};
app.post('/admin/settings', authMiddleware, adminOnly, (req, res) => {
// Update settings...
});3. Database Operations with Error Handling
import { NotFoundError, InternalServerError } from 'trapx';
class UserService {
async getUserById(id: string) {
try {
const user = await db.users.findUnique({ where: { id } });
if (!user) {
throw new NotFoundError('User not found', {
details: { userId: id }
});
}
return user;
} catch (error) {
if (error instanceof NotFoundError) throw error;
throw new InternalServerError('Database error', {
details: { operation: 'getUserById' },
cause: error
});
}
}
}4. Custom Error Transformation
import { createExpressErrorHandler, BaseError } from 'trapx';
const errorHandler = createExpressErrorHandler({
// Include stack traces in development
includeStackTrace: process.env.NODE_ENV === 'development',
// Custom error logging
logError: (error) => {
console.error('[App Error]:', {
message: error.message,
stack: error.stack,
timestamp: new Date().toISOString()
});
},
// Transform unknown errors
transformError: (error) => {
if (error.name === 'SequelizeUniqueConstraintError') {
return new ValidationError('Resource already exists', {
code: 'DUPLICATE_ENTRY',
details: error.fields
});
}
return new InternalServerError('Something went wrong');
}
});5. API Rate Limiting Example
import { ValidationError } from 'trapx';
import rateLimit from 'express-rate-limit';
const rateLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100,
handler: () => {
throw new ValidationError('Too many requests', {
code: 'RATE_LIMIT_EXCEEDED',
details: {
windowMs: '15m',
maxRequests: 100,
tryAgainIn: '15 minutes'
}
});
}
});
app.use('/api', rateLimiter);6. File Upload Validation
import { ValidationError } from 'trapx';
import multer from 'multer';
const upload = multer({
limits: {
fileSize: 5 * 1024 * 1024 // 5MB
}
});
app.post('/upload', upload.single('file'), (req, res) => {
if (!req.file) {
throw new ValidationError('No file uploaded', {
code: 'FILE_REQUIRED'
});
}
if (!['image/jpeg', 'image/png'].includes(req.file.mimetype)) {
throw new ValidationError('Invalid file type', {
code: 'INVALID_FILE_TYPE',
details: {
allowedTypes: ['image/jpeg', 'image/png'],
receivedType: req.file.mimetype
}
});
}
// Process file...
});Advanced Configuration
Custom Error Response Format
import { createExpressErrorHandler, BaseError } from 'trapx';
interface CustomError extends BaseError {
timestamp?: string;
requestId?: string;
}
const errorHandler = createExpressErrorHandler({
transformError: (error) => {
const customError: CustomError = new BaseError(error.message);
customError.timestamp = new Date().toISOString();
customError.requestId = generateRequestId();
return customError;
}
});Contributing
We welcome contributions! Please see our Contributing Guide for details.
License
MIT
