SyntaxStudy
Sign Up
Express.js Validation Errors and HTTP Status Codes
Express.js Beginner 1 min read

Validation Errors and HTTP Status Codes

Returning the correct HTTP status code is as important as the error message. A 400 Bad Request indicates the client sent malformed data; 401 Unauthorized means authentication is required or failed; 403 Forbidden means the client is authenticated but lacks permission; 404 Not Found is for missing resources; 409 Conflict is for duplicate entries; 422 Unprocessable Entity is for semantically invalid input that passed structural parsing; 429 Too Many Requests is for rate limiting. Never return 200 with an error body — this breaks client-side error detection. Returning 500 for client errors is equally harmful because it masks bugs and alarms on-call engineers unnecessarily. Taking five minutes to choose the right status code for each error path pays dividends in API clarity and observability. Log errors at the appropriate severity level: 4xx client errors are `warn` or `info` (the client made a mistake, not you); 5xx server errors are `error` and should trigger alerts. Structured JSON logging with a consistent schema — including `requestId`, `userId`, `method`, `path`, and `durationMs` — enables fast debugging in production.
Example
// middleware/errors.js  (expanded status-code handling)
const HTTP_ERRORS = {
    400: 'Bad Request',
    401: 'Unauthorized',
    403: 'Forbidden',
    404: 'Not Found',
    409: 'Conflict',
    422: 'Unprocessable Entity',
    429: 'Too Many Requests',
    500: 'Internal Server Error',
};

function errorHandler(err, req, res, next) {
    // Handle express-validator errors
    if (err.type === 'validation') {
        return res.status(422).json({ errors: err.details });
    }

    // Handle known Mongoose/Sequelize duplicate key
    if (err.code === 11000 || err.code === 'ER_DUP_ENTRY') {
        return res.status(409).json({ error: { message: 'Resource already exists', code: 'CONFLICT' } });
    }

    const status  = err.status  || 500;
    const message = err.message || HTTP_ERRORS[status] || 'Unknown error';
    const level   = status >= 500 ? 'error' : 'warn';

    console[level](JSON.stringify({
        level, status, message,
        requestId: req.requestId,
        method:    req.method,
        path:      req.path,
        stack:     process.env.NODE_ENV !== 'production' ? err.stack : undefined,
    }));

    res.status(status).json({
        error: { message: status >= 500 && process.env.NODE_ENV === 'production'
            ? HTTP_ERRORS[500] : message }
    });
}

module.exports = errorHandler;