nodejs / help

:sparkles: Need help with Node.js? File an Issue here. :rocket:
1.45k stars 278 forks source link

Error Handling #4266

Closed moezbenrebah closed 9 months ago

moezbenrebah commented 9 months ago

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

constructor(message: string, statusCode: number, showDetails = true) {
    super(message);

    this.statusCode = statusCode;
    this.status = `${statusCode}`.startsWith('4') ? 'fail' : 'error';

    this.isOperational = true;
    this.showDetails = showDetails;

    // Capture the stackTrace
    Error.captureStackTrace(this, this.constructor);
}

}

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;

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);
}

Operating system

Linux 22.04

Scope

NA

Module and version

Not applicable.

Pyrolistical commented 9 months ago

could fix the formatting in your post and show what is the expected result and actual? i don't understand the problem

moezbenrebah commented 9 months ago

I fixed my issue already, thank you any way