I am working with a partner on a project. He has written a lot of code in Node.js+Express, but we've been running into issues with the architecture.
To remedy this, my primary role has been to figure out the best way to architect a Node.js+Express application. I've run into two scenarios, dealing with errors, and I'd like some suggestions.
First, how do I capture top-level exceptions? The last thing I want is for a bug to completely kill the node process. I want to continue serving users in the face of any error.
Secondly, some errors are passed back via callbacks (we're using caolan / async). As part of each route handler, either we render a view (GET), redirect to another route (POST) and we want to redirect to an error screen with a custom error message. How can I make sure to capture this logic in one place?
First, how do I capture top-level exceptions? The last thing I want is for a bug to completely kill the node process. I want to continue serving users in the face of any error.
Edit: I think node's philosophy in general is that any uncaught exceptions should kill the process, and that you should run your node app under some kind of process monitor with appropriate logging facilities. The following advice is regarding any other errors you might encounter in your express route handlers etc.
Express has a general errorHandler, which should capture all thrown errors as well as everything passed as a parameter to next in your routes/middlewares, and respond with 500 Internal Server Error.
Secondly, some errors are passed back via callbacks (we're using caolan / async). As part of each route handler, either we render a view (GET), redirect to another route (POST) and we want to redirect to an error screen with a custom error message. How can I make sure to capture this logic in one place?
You could create a custom handleError, which you call in each callback like so:
async.series(..., function(err, results) {
if(err)
return handleError(req, res, err);
// ...
});
Or you could just pass the errors on with next(err) and implement your custom error handler as described here: http://expressjs.com/guide/error-handling.html
Top level exceptions:
You can use the uncaughtException event from process, but it's generally not recommended.
Often applications will go into a corrupted state (eg. you have some state which typically gets set, but the exception caused that not to happen) when an exception is thrown. Then, it will just cause more and more errors from there on onwards.
A recommended approach is to use something like forever to automatically restart the app in case it crashes. This way you will have the application in a sane state even after a crash.
Error handling in express:
You can create a new Error instance and pass it to the next callback in the chain.
Eg.
express.get('/some/url', function(req, res, next) {
//something here
if(error) {
next(new Error('blah blah'));
}
});
To handle the error from here on onwards, you can set an error handler. See express docs on error handling
Checkout the excellent log-handling module Winston: https://github.com/flatiron/winston
It allows you to configure exception handling in a manner that will not only log it, but will allow the process to continue. And, since these would obviously be serious issues, you can even configure Winston to send out emails on specific event types (like exceptions).
Related
An API method I've been using without problems on localhost:3000/userinfo suddenly gives an "Empty Reply From Server" when I upload that same code to my web server and send a POST request to myserver.com:3000/userinfo.
Even more strange is that if the JSON returned by user info does not contain an array of objects, there is no error locally or remotely.
I don't know how much detail will be useful, and how much will just clutter this question. This one is especially strange so I'd like to approach it very generally. In other words, I don't think I'm missing a semi colon this time because the API function works perfectly when run on a local server.
So without a whole lot of detail, the only thing I can suggest here is make sure your ports are correct. You said 4200 locally but its 3000 on your server? The error you're getting also suggests ( I'm assuming cURL? ) that it couldn't connect at all to your server, which furthers my hypothesis.
Alternatively, there could be an error in your API and its "crashing" without logging anything, which would also cause an empty reply error.
Be sure to capture unhandled exceptions using process events to log out errors!
https://nodejs.org/dist/latest-v10.x/docs/api/process.html#process_process_events
process.on('uncaughtException', function (error) {
console.error("GOT UNCAUGHT EXECPTION!", error)
process.exit(1);
})
Might be an idiotic question, but I was wondering why does, i.e. invoking Express' res.send() (a subclass of NodeJS' http.ServerResponse) more than once per a single request shut down a NodeJS server? Why doesn't it end the request while sending the first response and simply log the error, without crashing?
Express is just throwing an exception, then node handles it :
The 'uncaughtException' event is emitted when an uncaught JavaScript exception bubbles all the way back to the event loop. By default, Node.js handles such exceptions by printing the stack trace to stderr and exiting. doc
If you want to do something else, implement your own process.on('uncaughtException', (err) => {})
Or you could let it crash and use stuff like forever to bring it back up.
The only way I have found to "catch" EPIPE errors thrown asynchronously by a socket timing out or closing prematurely is to directly attach an event handler to the socket object itself, as demonstrated in the documentation here:
https://nodejs.org/api/errors.html
const net = require('net');
const connection = net.connect('localhost');
// Adding an 'error' event handler to a stream:
connection.on('error', (err) => {
// If the connection is reset by the server, or if it can't
// connect at all, or on any sort of error encountered by
// the connection, the error will be sent here.
console.error(err);
});
This works, but is in many cases unhelpful -- if you're accessing a database or another service that has a node driver, the request and socket objects are likely inaccessible from your app code.
The most obvious solution is "don't do things that generate these errors" but since any non-trivial application is dependent on other services, no amount of input-checking in advance can guarantee that the service on the other end won't hang up unexpectedly, throwing an EPIPE in your code and in all likelihood crashing Node.
So, the options for handling this situation seem to be:
Let the error crash your app and use nodemon or supervisor to automatically restart. This isn't clean, but it seems like the only way to really guarantee you'll get back up and running safely.
Write custom connection clients for dependent services. This let's you attach error handlers where known problems could occur. But it violates DRY and means that you're now on the hook for maintaining your own custom client code when otherwise reasonable open source solutions already exist. Basically, it adds a huge maintenance burden for a slightly cleaner solution to a fairly rare problem.
Am I missing something, or are those the best options available?
I can't seem to get domains to work on Azure Mobile Services (ZUMO). For example:
var myDomain = require('domain').create();
myDomain.on('error', function ()
{
console.log('got here');
});
myDomain.run(function() {
boo(); //throws
});
The on error handler of my domain will never get called. This exception will be caught by ZUMO and their 500 error will get returned. I'd prefer to trap the exception myself, log it, and return a 500 using my preferred JSON format. I realize that there is some global error trapping that ZUMO is doing but I would think that if I have a domain it should catch it before it bubbles up to the ZUMO wrapper. Any suggestions?
(ZUMO runs on Node 0.8.28)
The code you list will handle uncaught exceptions. Mobile Services scripts and the underlying async data operations are wrapped in try..catch blocks, so they will not invoke a domain error handler.
You should be handling errors in your scripts using normal error handling practices, i.e. try..catch blocks or error handling callbacks for promises. You can then return the appropriate response using res.send.
I am using node.js with domains and cluster to catch unexpected exceptions (programmer bugs) then restart gracefully. However we occasionally have programmers failing to add the final .catch().finally() to make sure that their code actually returns.
I can easily add a timeout that will throw an exception after a pre-specified time to make sure that these bad requests will not live forever. But what I'd like to do is to have the timeout pull information out of the domain to explain what had happened in the request so that we can have a log/notification/whatever that starts with a good indication of where the programmer bug was.
Is there any reasonable way to do that?
In case it matters, we are using express as a framework, express-domain-middleware to get domains/restart logic, and promises for async logic.
You don't actually need domains for this. If you use a promise library (like bluebird, or when, or even Q) or a recent version of Node (Namely, io.js) you don't have to remember using .catch on all chains and use the dedicated events:
process.on("unhandledRejection", function(e, reason){
// promise was rejected, even if no `catch` or `finally` attached
// restart the process
});
Note that domains are deprecated and will likely be removed in a future version of NodeJS, if you're using promises you already have catch safety so there's that.