Skip to content

Error handling ​

Throwing ​

Services throw helpers from src/lib/errors.js:

HelperStatusDefault code
badRequest(message, details?)400BAD_REQUEST
unauthorized(message?, code?)401UNAUTHORIZED
forbidden(message?, code?)403FORBIDDEN
notFound(message?)404NOT_FOUND
conflict(message, details?)409CONFLICT
unprocessable(message, code?, details?)422UNPROCESSABLE
serviceUnavailable(message, code?, details?)503SERVICE_UNAVAILABLE

Or new AppError(status, message, code, details) directly.

Async route handlers are wrapped in asyncHandler so rejections reach the error middleware.

Middleware mapping ​

src/middleware/errorHandler.js converts errors to JSON:

ErrorResponse
ZodError400 VALIDATION_ERROR with details
MulterError400 UPLOAD_ERROR (friendly message for oversized files)
Malformed JSON body400 INVALID_JSON
Prisma P2002 (unique violation)409 CONFLICT
Prisma P2025 (record not found)404 NOT_FOUND
AppErrorIts status, code, message and details
Anything else500 INTERNAL_ERROR with a generic message; full error logged

Unknown routes return 404 ROUTE_NOT_FOUND.

Response shape ​

json
{ "error": { "code": "INSUFFICIENT_BALANCE", "message": "…", "details": { } } }

details is omitted when undefined. See the error code reference.

Logging ​

AppErrors with status ≥ 500 are logged at warn; unexpected errors at error with method, path and stack.

Nexlm is a peer-to-peer facilitator and never holds customer Naira.