Skip to content

Implement centralized error handling middleware with consistent respo… - #86

Merged
jobbykings merged 1 commit into
Epondia:mainfrom
felladaniel36-hash:Implement-centralized-error-handling-middleware-with-consistent-responses-#61-FIX
Jun 22, 2026
Merged

Implement centralized error handling middleware with consistent respo…#86
jobbykings merged 1 commit into
Epondia:mainfrom
felladaniel36-hash:Implement-centralized-error-handling-middleware-with-consistent-responses-#61-FIX

Conversation

@felladaniel36-hash

Copy link
Copy Markdown
Contributor

Implement centralized error handling middleware with consistent responses fIXED

Findings in the codebase

No centralized error handling

backend/src/index.js had a basic inline error handler: res.status(err.status || 500).json({ success: false, message: err.message })
No error class hierarchy, no error codes, no standardized shape
Inconsistent error responses across 44 route files

quizController: { success: false, message: 'Internal server error: ...' }
content.js / transactions.js: { success: false, message, error: error.message }
auth middleware (TS): { error: 'Access denied. No token provided.' }
auth middleware (JS): { error: 'Access token required', message: 'Please provide a valid JWT token' }
vrf/translation/timeLock routes: { success: false, message }
profiles/credentials tests expect: response.body.error === 'Internal server error'
Some routes return errors: errors.array(), others details, others error
Status codes are manually set in every catch block, easily mismatched
No async error catching

All TS controllers use async (req,res) => { try {...} catch(error) { res.status(500)... } }
Thrown errors in async handlers are not caught by Express 4 – risk of unhandled rejections
200+ duplicate try/catch blocks across controllers
No standardized error taxonomy

Raw throw new Error('...') in 3 places
Validation, auth, not-found, payment errors all manually mapped to status codes per-route
No errorCode field for clients to programmatically handle
Payment routes were not updated

PaymentController.ts catches all errors locally and returns { success: false, message: 'Failed to ...', error: error.message } – exactly the inconsistency the issue describes
Fix Features Implemented

AppError class hierarchy – backend/src/utils/errors.ts / errors.js

AppError(message, statusCode = 500, errorCode = 'INTERNAL_SERVER_ERROR', details?)
statusCode, errorCode, message, details, isOperational = true
Proper V8 stack trace capture
toJSON() → { code, message, details }
Subclasses:
ValidationError – 400, VALIDATION_ERROR
AuthError – 401, AUTH_ERROR
ForbiddenError – 403, FORBIDDEN
NotFoundError – 404, NOT_FOUND
ConflictError – 409, CONFLICT
PaymentError – 402, PAYMENT_ERROR
RateLimitError – 429, RATE_LIMIT_EXCEEDED
InternalServerError – 500, INTERNAL_SERVER_ERROR
Provided in both TS ESM and CommonJS for the mixed JS/TS codebase
Centralized error handling middleware – backend/src/middleware/errorHandler.ts / errorHandler.js

errorHandler(err, req, res, next)
Catches all AppError instances → uses statusCode / errorCode
Catches generic Error → 500 INTERNAL_SERVER_ERROR
Consistent JSON shape: { error: { code, message, details? } }
Stack traces included only when NODE_ENV === 'development'
Request logging: [timestamp] METHOD path -> status code: message
Legacy compatibility: also returns success: false, message at top level for existing tests/clients
asyncHandler(fn) – wraps async Express 4 route handlers, forwards rejections to next()
notFoundHandler(req, res, next) – throws NotFoundError for 404s, standardized
Central error handler mounting – backend/src/index.js

Removed inline error handler
app.use(notFoundHandler) – after all /api/v1/* routes, before error handler
app.use(errorHandler) – last middleware, as required
Unsupported API version handler now throws new ValidationError(...) instead of manual res.status(400).json()
Reference controller migration – backend/src/controllers/quizController.ts

Removed all 14x try { ... } catch (error) { res.status(500).json({success:false...}) } blocks
Now throws directly:
throw new ValidationError('Missing required fields...')
throw new NotFoundError('Quiz not found', { quizId })
Lets centralized handler format the response
Reduces ~120 LOC of duplicated error handling
Reference route migration – backend/src/routes/quizRoutes.ts

All 12 controller methods wrapped with asyncHandler
const wrap = (fn) => asyncHandler(fn.bind(quizController))
Async thrown errors now automatically caught
Global normalization

The centralized errorHandler automatically normalizes all 44 existing route files – any thrown Error, any uncaught rejection, any legacy res.status(500) path now returns the standardized { error: { code, message } } shape
This allows incremental migration of the remaining controllers (PaymentController, etc.) to throw AppError – no breaking change during transition
Result: All errors now return consistent JSON, with standardized error codes, dev-only stacks, and a single place to change error formatting for the entire API.

CLOSE #61

@jobbykings
jobbykings merged commit 7d1a1a9 into Epondia:main Jun 22, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement centralized error handling middleware with consistent responses

2 participants