/** * BaseRouteHandler * * Base class for all route handlers providing: * - Automatic try-catch wrapping with error logging * - Integer parameter validation * - Required body parameter validation * - Standard HTTP response helpers * - Centralized error handling */ import { Request, Response } from 'express'; import { logger } from '../../../utils/logger.js'; import { AppError } from '../../server/ErrorHandler.js'; export abstract class BaseRouteHandler { /** * Wrap handler with automatic try-catch and error logging */ protected wrapHandler( handler: (req: Request, res: Response) => void | Promise ): (req: Request, res: Response) => void { return (req: Request, res: Response): void => { try { const result = handler(req, res); if (result instanceof Promise) { result.catch(error => this.handleError(res, error as Error)); } } catch (error) { logger.error('HTTP', 'Route handler error', { path: req.path }, error as Error); this.handleError(res, error as Error); } }; } /** * Parse and validate integer parameter * Returns the integer value or sends 400 error response */ protected parseIntParam(req: Request, res: Response, paramName: string): number | null { const value = parseInt(req.params[paramName], 10); if (isNaN(value)) { this.badRequest(res, `Invalid ${paramName}`); return null; } return value; } /** * Validate required body parameters * Returns true if all required params present, sends 400 error otherwise */ protected validateRequired(req: Request, res: Response, params: string[]): boolean { for (const param of params) { if (req.body[param] === undefined || req.body[param] === null) { this.badRequest(res, `Missing ${param}`); return false; } } return true; } /** * Send 400 Bad Request response */ protected badRequest(res: Response, message: string): void { res.status(400).json({ error: message }); } /** * Send 404 Not Found response */ protected notFound(res: Response, message: string): void { res.status(404).json({ error: message }); } /** * Centralized error logging and response * Checks headersSent to avoid "Cannot set headers after they are sent" errors */ protected handleError(res: Response, error: Error, context?: string): void { // [APPROVED OVERRIDE]: Worker routes need centralized AppError translation so // status/code/details stay consistent across every HTTP handler. logger.failure('WORKER', context || 'Request failed', {}, error); if (!res.headersSent) { const statusCode = error instanceof AppError ? error.statusCode : 500; const response: Record = { error: error.message }; if (error instanceof AppError && error.code) { response.code = error.code; } if (error instanceof AppError && error.details !== undefined) { response.details = error.details; } res.status(statusCode).json(response); } } }