Data Validation, Error Handling, and API Design PatternsLesson 3.2
Consistent API error response format design
error response envelope, error codes, error messages, stack traces in development, AppError class, centralized error handler, RFC 7807
Consistent Error Response Design
Inconsistent error responses are one of the top complaints API consumers have. Pick a format and stick to it everywhere. RFC 7807 (Problem Details) is a good reference, but even a simple custom envelope works โ the key is consistency.
AppError Class
// utils/AppError.js
class AppError extends Error {
constructor(message, statusCode, code) {
super(message);
this.statusCode = statusCode;
this.code = code; // machine-readable string e.g. 'USER_NOT_FOUND'
this.isOperational = true; // distinguish from programming errors
}
}
module.exports = AppError;Centralized Error Handler
// middleware/errorHandler.js
const AppError = require('../utils/AppError');
module.exports = (err, req, res, next) => {
const status = err.statusCode || 500;
const response = {
error: {
code: err.code || 'INTERNAL_ERROR',
message: err.message || 'Something went wrong'
}
};
// Only expose stack in development
if (process.env.NODE_ENV === 'development') {
response.error.stack = err.stack;
}
res.status(status).json(response);
};// Usage in controller
throw new AppError('User not found', 404, 'USER_NOT_FOUND');The isOperational flag distinguishes expected errors (user not found, invalid input) from programming errors (undefined is not a function). Log non-operational errors with full context for debugging; surface only safe messages to clients.
