SEAM API processes every thrown error through a two-middleware pipeline registered at the very end ofDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/TheSerchCp/SEAM-API/llms.txt
Use this file to discover all available pages before exploring further.
app.js. The first middleware catches requests to unknown routes and converts them into a typed NotFoundError. The second middleware — the global errorHandler — inspects every error that reaches it, maps it to the right HTTP status code, emits a Socket.IO notification to the requesting client, and responds with a consistent JSON envelope. Application internals are never exposed to the caller.
Error Response Envelope
Every error response, regardless of type or origin, uses the same shape:success is always false on error. The message field is safe for display in a UI — SEAM API never leaks stack traces, SQL queries, or internal state to the HTTP response.
The Error Class Hierarchy
AppError — Base Class
All intentional errors in SEAM API extend AppError, which itself extends the native Error class:
| Property | Type | Purpose |
|---|---|---|
message | string | Human-readable description passed to the client |
statusCode | number | HTTP status code for the response |
isOperational | boolean | true means the error was intentional and its message is safe to expose |
Error.captureStackTrace excludes the AppError constructor frame from the stack trace, making debugging cleaner.
Only errors where
isOperational is true expose their message to the HTTP response. Unexpected errors (bugs, network failures, unhandled exceptions) always return the generic string "Error interno del servidor", protecting internal implementation details from callers.Built-in HTTP Error Classes
Six concrete error classes are exported fromHttpErrors.js. Each extends AppError with a fixed status code and a sensible Spanish-language default message.
BadRequestError → 400
Default:
"Solicitud inválida". Thrown by the validate middleware when field rules fail, or directly by controllers when input is semantically wrong.UnauthorizedError → 401
Default:
"No autorizado". Thrown by JWT authentication middleware when the token is missing, malformed, or expired.ForbiddenError → 403
Default:
"Acceso denegado". Thrown by role-based access control when the authenticated user lacks permission for the requested resource.NotFoundError → 404
Default:
"Recurso no encontrado". Thrown by controllers when a queried record does not exist, or automatically by notFoundHandler for unknown routes.ConflictError → 409
Default:
"Conflicto con el estado actual". Thrown when the requested change would violate a business rule or uniqueness constraint.UnprocessableEntityError → 422
Default:
"Entidad no procesable". Thrown when the request is structurally valid but semantically incorrect (e.g., date range inversion).MySQL Error Mapping
MySQL constraint violations surface as error objects with acode property rather than statusCode. The error handler intercepts these before they reach the generic 500 path and converts them into meaningful HTTP responses:
| MySQL Code | HTTP Status | Message Returned to Client |
|---|---|---|
ER_DUP_ENTRY | 409 Conflict | El registro ya existe (valor duplicado) |
ER_NO_REFERENCED_ROW_2 | 400 Bad Request | Referencia a un registro inexistente |
ER_ROW_IS_REFERENCED_2 | 409 Conflict | No se puede eliminar: existen registros relacionados |
MYSQL_ERROR_MAP inside error.middleware.js. You never need to catch MySQL errors individually in your service layer — let them bubble up and the handler converts them automatically.
The 404 Not-Found Handler
notFoundHandler is registered after all routes and before errorHandler. It catches every request that did not match any defined route and forwards a NotFoundError to the error pipeline:
404 response rather than Express’s default HTML error page, keeping the API consistent for all callers.
The Global Error Handler
errorHandler is the last middleware in app.js. Express identifies it as an error handler by its four-parameter signature (err, req, res, next):
- Socket.IO notification — before anything else, the requesting socket’s loader is dismissed via
emitError. - MySQL errors — checked by
err.codemembership inMYSQL_ERROR_MAP. - Operational errors — checked by
instanceof AppError && isOperational. - Unhandled errors — everything else becomes a
500with the generic message; the full error is logged server-side with[ERROR NO CONTROLADO].
HTTP Status Code Reference
| Status | Class / Source | When It Occurs |
|---|---|---|
400 | BadRequestError / ER_NO_REFERENCED_ROW_2 | Validation failed, or FK points to non-existent row |
401 | UnauthorizedError | JWT token absent, expired, or invalid |
403 | ForbiddenError | Authenticated but insufficient role permissions |
404 | NotFoundError / notFoundHandler | Resource or route not found |
409 | ConflictError / ER_DUP_ENTRY / ER_ROW_IS_REFERENCED_2 | Duplicate value or deletion blocked by related records |
422 | UnprocessableEntityError | Request well-formed but semantically invalid |
500 | Unhandled / unexpected | Any uncaught exception — details logged, not exposed |
Socket.IO Error Emission
When an error occurs during an operation that has acurrentOperation stored in the async request context, errorHandler calls emitError before sending the HTTP response. This dismisses any loading spinner on the client without requiring the frontend to rely solely on the HTTP response:
Registering the Middleware
Both middlewares must be registered after all routes inapp.js: