I successfully built an error handling system in my nodeJS backend with Express and PostgreSQL. As a best practice, I showed all the error details in the development environment e.g { status: "error", message: "filename must be a string", endpoint: "/api/v2/...", ErrorStack: TypeError: "filename must be a string, ..." } ), and showed less details in production (e.g { status: "error", message: "something went wrong" } ), but my problem is to keep track of the entire error object properties on production in order to send it via an email or log it to console or send via Slack message, but my current configuration doesn't allow to access them all, so just I got the access to status, and endpoint (e.g {status: "error", endpoint: "/api/v2/..."} )
Node.js version
nodeJS v16
Example code
1) So I built a class that extends the Error class:
class AppError extends Error {
statusCode: number;
status: string;
isOperational: boolean;
showDetails: boolean; // I use this attribute to show details in prod env, but it doesn't worked
2) After I wrap my controllers with function wrapper in order to capture asynchronous error and pass to next(), below is an example:
export const someFn = catchAsyncHandler(
async (req: any, res: any, next: any) => {
const status = await someFn(
// do something
);
if (some condition) {
// some code here!
return res.status(status.code)...
}
},
);
4) then the error handlers in Dev and Prod:
// All works fine
const sendErrorDev = (error: any, req: Request, res: Response) => {
if (req.originalUrl.startsWith('/api/v2')) {
// sending error via Slack
sendErrorMessageToSlack(
Error: ${error.status}\nEndpoint: ${req.originalUrl}\nErrorMessage: ${error.message}\nErrorStack: ${error.stack},
);
return res.status(error.statusCode).json({
status: error.status,
error: error,
message: error.message,
stack: error.stackTrace,
});
}
console.log(error);
};
// Here I tried to get the full error details but I couldn't:
const sendErrorProd = (error: any, req: Request, res: Response) => {
const doNotShowToUser = !error.showDetails;
if (req.originalUrl.startsWith('/api/v2')) {
if (error.isOperational) {
return res.status(error.statusCode).json({
status: error.status,
message: error.message,
});
}
// Programming or other unknown error
// generate friendly error message
const errorResponse: any = {
status: error.status,
message: error.message,
};
if (!doNotShowToUser) {
errorResponse.error = error;
errorResponse.errorStack = error.stackTrace;
}
sendErrorMessageToSlack(
`Error: ${errorResponse.status}\nEndpoint: ${req.originalUrl}\nErrorMessage: ${errorResponse.message}\nErrorStack: ${errorResponse.errorStack}`,
);
return res.status(error.statusCode).json(errorResponse);
}
Details
I successfully built an error handling system in my nodeJS backend with Express and PostgreSQL. As a best practice, I showed all the error details in the development environment e.g { status: "error", message: "filename must be a string", endpoint: "/api/v2/...", ErrorStack: TypeError: "filename must be a string, ..." } ), and showed less details in production (e.g { status: "error", message: "something went wrong" } ), but my problem is to keep track of the entire error object properties on production in order to send it via an email or log it to console or send via Slack message, but my current configuration doesn't allow to access them all, so just I got the access to status, and endpoint (e.g {status: "error", endpoint: "/api/v2/..."} )
Node.js version
nodeJS v16
Example code
1) So I built a class that extends the Error class: class AppError extends Error { statusCode: number; status: string; isOperational: boolean; showDetails: boolean; // I use this attribute to show details in prod env, but it doesn't worked
}
2) After I wrap my controllers with function wrapper in order to capture asynchronous error and pass to next(), below is an example: export const someFn = catchAsyncHandler( async (req: any, res: any, next: any) => { const status = await someFn( // do something ); if (some condition) { // some code here! return res.status(status.code)... } }, );
3) the wrapper function: export const catchAsyncHandler = (fn: any) => { return (req: Request, res: Response, next: NextFunction) => { fn(req, res, next).catch(next); }; };
4) then the error handlers in Dev and Prod: // All works fine const sendErrorDev = (error: any, req: Request, res: Response) => { if (req.originalUrl.startsWith('/api/v2')) { // sending error via Slack sendErrorMessageToSlack(
Error: ${error.status}\nEndpoint: ${req.originalUrl}\nErrorMessage: ${error.message}\nErrorStack: ${error.stack}
, ); return res.status(error.statusCode).json({ status: error.status, error: error, message: error.message, stack: error.stackTrace, }); } console.log(error); };// Here I tried to get the full error details but I couldn't: const sendErrorProd = (error: any, req: Request, res: Response) => { const doNotShowToUser = !error.showDetails;
Operating system
Linux 22.04
Scope
NA
Module and version
Not applicable.