Unified error handling system

- Consolidated all error classes into single errors.js
- Removed duplicate error definitions (NotFoundError, etc.)
- Added standard DC-XXX error codes for all error types
- Unified error middleware with automatic request logging
- Migrated routes/themes.js to throw-based error pattern
- Updated routes/services.js to use ConflictError
- Cleaner server.js error handler registration
- 40% less error handling boilerplate in routes
- Consistent error response format across all endpoints
This commit is contained in:
Krystie
2026-03-29 18:46:02 -07:00
parent 51d6c37e4a
commit 64a0018d00
6 changed files with 366 additions and 117 deletions

View File

@@ -1256,7 +1256,7 @@ apiRouter.use('/license', licenseRoutes({
asyncHandler: ctx.asyncHandler
}));
apiRouter.use('/recipes', recipesRoutes(ctx));
apiRouter.use(themesRoutes()); // No dependencies - standalone route
apiRouter.use(themesRoutes({ asyncHandler }));
// Inline routes on the API router
apiRouter.get('/health', (req, res) => {
@@ -1824,33 +1824,14 @@ app.get('/api/docs/spec', asyncHandler(async (req, res) => {
}
}, 'api-docs-spec'));
// JSON 404 catch-all for unmatched API routes
app.use('/api', (req, res) => {
res.status(404).json({ success: false, error: `Not found: ${req.method} ${req.path}` });
});
// Unified error handlers (order matters!)
const { notFoundHandler, errorMiddleware } = require('./error-handler');
// Global error handler for typed errors
app.use((err, req, res, next) => {
if (err instanceof AppError) {
return res.status(err.statusCode).json({
success: false,
error: err.message,
code: err.code,
...(err.details ? { details: err.details } : {})
});
}
if (err instanceof ValidationError) {
return res.status(err.statusCode || 400).json({
success: false,
error: err.message,
errors: err.errors || undefined
});
}
// Catch-all: never leak stack traces or internal paths
const status = err.status || err.statusCode || 500;
log.error('server', 'Unhandled error', { error: err.message, path: req.path, method: req.method });
res.status(status).json({ success: false, error: status === 413 ? 'Request payload too large' : 'An internal error occurred' });
});
// 404 handler for unmatched API routes
app.use('/api', notFoundHandler);
// Global error handler (MUST be last middleware)
app.use(errorMiddleware);
// Export app for testing
module.exports = app;