Node.js Error Handling Patterns

matt
Matthew Gros · Nov 28, 2025

TLDR

Use try/catch with async/await, create custom error classes, centralize error handling, log everything.

Node.js Error Handling Patterns

Errors Crash Servers

Unhandled errors in Node.js kill the process.

Async/Await Error Handling

async function getUser(id) {
    try {
        const user = await db.users.findById(id);
        if (!user) {
            throw new NotFoundError('User not found');
        }
        return user;
    } catch (error) {
        logger.error('Failed to get user', { id, error });
        throw error;
    }
}

Custom Error Classes

class AppError extends Error {
    constructor(message, statusCode = 500) {
        super(message);
        this.statusCode = statusCode;
        this.isOperational = true;
    }
}

class NotFoundError extends AppError {
    constructor(message = 'Not found') {
        super(message, 404);
    }
}

class ValidationError extends AppError {
    constructor(message, errors = []) {
        super(message, 422);
        this.errors = errors;
    }
}

Centralized Error Handler

// Express middleware
function errorHandler(err, req, res, next) {
    logger.error(err);

    if (err.isOperational) {
        return res.status(err.statusCode).json({
            error: {
                message: err.message,
                ...(err.errors && { errors: err.errors })
            }
        });
    }

    // Unknown error - don't leak details
    res.status(500).json({
        error: { message: 'Internal server error' }
    });
}

app.use(errorHandler);

Catch Unhandled Rejections

process.on('unhandledRejection', (reason, promise) => {
    logger.error('Unhandled Rejection:', reason);
    // In production, you might want to exit and let process manager restart
});

process.on('uncaughtException', (error) => {
    logger.error('Uncaught Exception:', error);
    process.exit(1);  // Exit - state is uncertain
});

Async Route Wrapper

const asyncHandler = (fn) => (req, res, next) => {
    Promise.resolve(fn(req, res, next)).catch(next);
};

// Usage
app.get('/users/:id', asyncHandler(async (req, res) => {
    const user = await getUser(req.params.id);
    res.json(user);
}));

About the Author

matt

I build and ship automation-driven products using Laravel and modern frontend stacks (Vue/React), with a focus on scalability, measurable outcomes, and tight user experience. I’m based in Toronto, have 13+ years in PHP, and I also hold a pilot’s license. I enjoy working on new tech projects and generally exploring new technology.