The best solution is to rely on asynchronous code.
Asynchronous code show not throw but rather pass the error to the Callback function as the first argument to be handled by the callee. The problem with a lot of modules is that people seem to throw whenever they think it is right to do so and this happens a lot more often than actual syntax exceptions, type exceptions or any exception is thrown by V8. If you don't want your app to crash in production use well-written modules that are truly async and rely on callbacks rather than try/catch and if you really need to rely on a sync module then wrap it around try/catch but there is almost always an async module out there that even though might not perform any I/O passes the error to the callback.
Moreover try/catch does have a performance overhead (albeit tiny) that would put strain on resources if overused (don't wrap every module in try/catch). From experience I suggest if you are developing something that goes into production then try to choose your modules carefully rather than catching their errors later on. process.on('uncaughtException') is a last resort and usually ideal for logging the exceptions.
As an ultimate solution I suggest that you use a process manager. My personal choice is PM2. It will attempt to gracefully restart your code and handles exceptions pretty well.
References:
Try/Catch performance overhead.
Try/Catch performance overhead (recent)
Using PM2 in production
Isaac Schlueter on async code and throw