UNPKG

trapx

Version:

A plug-and-play middleware for standardized error handling in TypeScript-based Express.js applications

245 lines (193 loc) • 5.44 kB
# 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 ```bash npm install trapx # or yarn add trapx ``` ## Quick Start ```typescript 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 ```typescript 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 ```typescript 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 ```typescript 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 ```typescript 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 ```typescript 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 ```typescript 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 ```typescript 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](CONTRIBUTING.md) for details. ## License MIT