5

I am looking for a way to handle errors in a Node.js application without getting lots of log entries for the same error event, one from each each affected layer.

This might actually apply to any language, but I still don't understand how to do it correctly. The questions are always who handles the errors, does it make sense to continually throw to the top, and who logs what?

Let's take a simple example of a user system where you can retrieve users from a database and "log in" if credentials match.

Database layer

// connects to a mongo db 
class UserDatabase(){
   async getUser(filter){
       // this can throw if connection is dead
       let user = await this.collections.users.findOne(filter);
       return user;
   }
}

Application layer

// makes calls to the db class to "login" a user
class UserApp {
    async loginUser(email, password){
       let user = await this.db.getUser({email});
       if(!user){
           return false;
       }
       if(user.password === options.password){
           return true;
       }
    }
}

REST layer

// calls the API from REST calls
class UserRestApi {
    async loginUser(request, response){
        let result = await this.app.loginUser(request.body.email, request.body.password);
        if(result){
            response.status = 200;
        }
        else {
            response.status = 401;
        }
        response.end();
    }
}

At this moment there is 0 error handling. We can fail almost everywhere with uncaught exceptions. But is there ever any point in catching at the lowest level? Suppose getUser() fails because the mongodb connection dies. I can catch it and log it there, but then do I throw it again, and again, and again, and log it again, and again, and again? Does that make sense? I would end up in this case with 2-3 repeated logs.

I'm just not sure how to approach this.

5
  • I don't know why your question was downvoted, I believe it's on-topic. Sadly I haven't touched Node.js in over 4 years so I don't know what the best solution is here. Commented Oct 19, 2019 at 18:09
  • 1
    I don't know. The whole point of this site used to be about asking questions. Now it's really just about being the first to mark duplicates or downvote things. Commented Oct 19, 2019 at 18:13
  • If @downvoters would be kind enough to leave a comment to help OP improve their question instead of drive-by downvoting that would be awesome :) I see nothing wrong with the question, it's an architecture question about error propagation throughout the application layer, and is very well-written. Commented Oct 19, 2019 at 18:16
  • @DocBrown language clarification would definitely make it far more on-topic, good call! Commented Oct 19, 2019 at 20:17
  • 1
    I took the freedom to edit your question into a form which avoids further downvotes for the reasons listed here. Please double check if I got your intentions right.
    – Doc Brown
    Commented Oct 20, 2019 at 7:14

3 Answers 3

4

General Principles of Error Handling

Exceptions should be handled in the layer of code that can actually do something about the error.

The "log and rethrow" pattern is often considered an antipattern (for exactly the reason you mentioned, leads to a lot of duplicate code and doesn't really help you do anything practical.)

The point of an exception is that it is "not expected". If the layer of code you are working in can't do something reasonable to continue successful processing when the error happens, just let it bubble up.

If the layer of code you are working in can do something to continue when the error happens, that is the spot to handle the error. (And returning a "failed" http response code counts as a way to "continue processing". You are saving the program from crashing.)

Node.js web server error handling (using a library like Koa/Express)

I would strongly recommend using a framework like Koa.js (the "successor" to the very popular Express framework) if you are building an http server in Node.js.

These frameworks give you a very clean and simple toolset to build up your webapi with ease. (and they both give you default error handling out of the box)

0
2

The standard way is to let the exception bubble up and the webserver will convert it to a 500 with error message.

You can improve upon this by hiding the error message from non-local connections.

You can improve upon that by having an unhandler exception handler which logs all unhandled errors.

Now, whether you can do that in node or not I don't know.

.. Ok Just reading up and.. omg node crashes if it gets an unhandled error?!?!?

https://stackoverflow.com/questions/7310521/node-js-best-practice-exception-handling

So the answer is follow the crazy complicated and flawed error event stuff or upgrade to a sensible language/webserver

10
  • Of course node.js crashes if it gets an unhandled error. What would you expect it to do? It is just a basic code runtime environment. Would you expect a Java console program to not crash on an unhandled exception? The better suggestion for node would be to use a library like express/koa to handle the basic http server stuff. And setup a basic error handling middlewear handle exception cases.
    – Caleb
    Commented Oct 20, 2019 at 4:32
  • 2
    The information behind the link is fine in this answer, but everything else does not seem to apply to node.js. And "use a different web server" is pretty much a non-answer.
    – Doc Brown
    Commented Oct 20, 2019 at 7:00
  • I'd expect that call to crash and return an error code, while the envrionment continues to accept and process other calls
    – Ewan
    Commented Oct 20, 2019 at 8:31
  • @doc it seems to me that 'use another web server' is the answer here. This question would not come up in asp or php or ruby on rails etc because the framework provides the basic function of unhandled exception processing.
    – Ewan
    Commented Oct 20, 2019 at 8:35
  • 2
    @ewan - asp != .net, Ruby "on rails" != Ruby. Those are frameworks on top of the code "runtime environment". Node.js is the runtime environment. Express (a framework in node.js) DOES handle errors automatically for you. expressjs.com/en/guide/error-handling.html
    – Caleb
    Commented Oct 20, 2019 at 18:19
-3

Fastify can help a bit to avoid writing boilerplate for generating proper error response for the client, because it does it automatically if handler function returns Error instance (instance that extends Error and holds response status)

2
  • 2
    I logged in specifically to comment here. No, this is not what you want to do and avoid this pattern at all costs. Don't encapsulate the error in another Error object. Also, do not install another library and expect it to handle all the exception handling for you.
    – Steven
    Commented Jan 5, 2022 at 14:29
  • 2
    This is the complete antithesis (and suppression) of the Structured Exception Handling mechanism! It requires you to do far more work than proper use of SEH would. I can see why you'd think you want to use it, solely to take advantage of Fastify's "error-wrapping" capability but, frankly, the notion of wrapping Exceptions in Errors is just plain wrong.
    – Phill W.
    Commented Aug 23, 2024 at 10:52

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.