node-js-api-response-ts
Version:
Unified API response and error handling for Express.js in TypeScript. This package provides a middleware for consistent API responses and error handling in Express applications, making it easier to manage API responses and errors in a standardized way.
96 lines (95 loc) • 3.67 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.errorHandler = void 0;
const os_1 = __importDefault(require("os"));
const ApiError_1 = require("./ApiError");
const errorHandler = (err, req, res, next) => {
var _a;
let statusCode = err.statusCode || 500;
let message = err.message || 'Something went wrong. Please try again later.';
const status = (_a = err.status) !== null && _a !== void 0 ? _a : false;
const stack = err.stack;
const name = err.name || 'Error';
const errCode = err.code;
// In development mode, log the stack trace for debugging
const isProduction = process.env.NODE_ENV === 'production';
const log = `[${os_1.default.hostname()}] [${statusCode}] | ${req.method} ${req.originalUrl} - ${name} | ${message}`;
console.error(isProduction && stack ? log : `${log} | ${stack}`);
const errorResponse = (statusCode, message) => {
const response = {
status,
statusCode,
message,
};
if (!isProduction) {
response.name = err.name;
response.errCode = errCode;
response.stack = err.stack;
}
res.status(statusCode).json(response);
};
// Check if the error is an instance of ApiError
if (err instanceof ApiError_1.ApiError)
return errorResponse(statusCode, message);
// Mongo Duplicate Key
if (err.code === 11000) {
statusCode = 409;
const fields = err.keyValue
? Object.entries(err.keyValue).map(([key, val]) => `${key}: ${val}`)
: [];
message = `Duplicate entry: ${fields.join(', ')} already exists.`;
return errorResponse(statusCode, message);
}
// Mongoose validation
if (err.name === 'ValidationError') {
statusCode = 400;
message = Object.values(err.errors).map((e) => e.message).join(', ');
return errorResponse(statusCode, message);
}
// CastError for invalid ObjectId
if (err.name === 'CastError') {
return errorResponse(400, `Invalid ${err.path}: ${err.value}`);
}
// JWT errors
if (err.name === 'TokenExpiredError')
return errorResponse(401, 'Access token has expired');
if (err.name === 'JsonWebTokenError')
return errorResponse(401, 'Invalid access token');
if (err.name === 'NotBeforeError')
return errorResponse(401, 'Access token not active yet');
// System Errors
const systemErrors = {
ENOENT: [404, 'File or resource not found.'],
EACCES: [403, 'Permission denied.'],
ECONNREFUSED: [503, 'Connection refused.'],
ETIMEDOUT: [504, 'Request timed out.'],
ECONNRESET: [502, 'Connection was reset.'],
};
if (err.code && systemErrors[err.code]) {
const [code, msg] = systemErrors[err.code];
return errorResponse(code, `${msg} (${err.code})`);
}
// JS Errors
const jsErrorTypes = [
'SyntaxError',
'ReferenceError',
'TypeError',
'RangeError',
'URIError',
'EvalError',
'AggregateError',
];
if (jsErrorTypes.includes(name)) {
return errorResponse(500, `Unexpected ${name}: ${message}`);
}
// Axios/Fetch
if (err.isAxiosError || /fetch|network/i.test(message)) {
return errorResponse(502, `External API/network request failed: ${message}`);
}
// Default fallback
return errorResponse(statusCode, message);
};
exports.errorHandler = errorHandler;