I have been reading for hours on exception handling in Node. I understand the cons of using uncaughtException, I understand that shutting down the process is good for preventing any "unknown state" where "anything can happen". I understand that using domains is the way to go, and I understand how to properly implement domains, specifically Explicit Binding...
...but I'm still not getting any results for just basic error handling.
I would like to be able to just catch any uncaught exceptions for the purpose of logging. I don't mind killing the process or anything else deemed "undesirable". I just want a log.
I don't feel like I should have to wrap everything in a try/catch or use some library to emit errors... please correct me if I'm wrong and I will change my ways.
I am using Node and Express and I have the following simple code:
var express = require('express');
var domain = require('domain');
var serverDomain = domain.create();
serverDomain.on('error', function(err) {
console.log("SERVER DOMAIN ERROR: " + err.message);
});
serverDomain.run(function() {
var app = express();
app.get('/testing', function() {
app.nonExistent.call(); // this throws an error
});
var server = app.listen(8000, function() {
console.log('Listening on port %d', server.address().port);
});
});
The error shows up in the console, but the console never receives the "SERVER DOMAIN ERROR..." message. I have also tried wrapping the request/response in their own domain as well, to no avail. Even more disappointing is the fact that using the following does not work either:
process.on('uncaughtException', function(err) {
console.log('uncaughtException caught the error');
});
Am I doing something wrong? Where do I go from here? How can I catch the above error?
You can use connect-domain.
The problem is that the exception happens during Connect's routing, which has both a try/catch block around its execution, as well as a default error handler which prints out stack trace details when running in a non-production mode. Since the exception is handled inside of Express, it never reaches your outer layer for the domains to handle.
Here is an example why to use connect-domain package instead of domain.
http://masashi-k.blogspot.com/2012/12/express3-global-error-handling-domain.html
var express = require('express');
var connectDomain = require('connect-domain');
var app = express();
app.use(connectDomain());
app.get('/testing', function() {
app.nonExistent.call(); // this throws an error
});
app.use(function(err, req, res, next) {
res.end(err.message); // this catches the error!!
});
var server = app.listen(8000, function() {
console.log('Listening on port %d', server.address().port);
});
This happens because Express handles by itself the errors that may appear and in this way it simplifies your work. See Express Guide for error handling. You should use the structure below to handle the errors that may appear:
app.use(function(err, req, res, next){
console.error(err.stack);
res.send(500, 'Something broke!');
});
Related
Hello totally awesome cool beautiful people! I am trying to get this custom middleware function to run upon an error. I am using express and I have a custom middleware function which should run when a new error is thrown.
All of this is written in express. I have read the docs and am still struggling to get this correct. Here is the middleware function.
const errorHandler = (err, req, res, next) => {
console.log("running error handler middleware")
const statusCode = res.statusCode ? res.statusCode : 500
res.status(statusCode)
res.json({
message: err.message,
stack: process.env.NODE_ENV === "production" ? null : err.stack
})
}
module.exports = errorHandler
Here is my entry point
const express = require('express')
const dotenv = require('dotenv').config()
const colors = require('colors')
const errorHandler = require('./middleware/errorMiddleware')
const connectDB = require('./config/db')
const port = process.env.PORT || 5000
const app = express()
connectDB()
//these lines of middleware allow us to grab info from the req.body
app.use(express.json())
app.use(express.urlencoded({extended:false}))
app.use('/api/goals', require('./routes/goalRoutes'))
app.use('/api/users', require('./routes/userRoutes'))
//pulling in our error handler middleware
app.use(errorHandler)
app.listen(port, () => {
console.log(`Server started on port ${port}`)
})
I have confirmed my file structure is correct and I am pulling the middleware in from the correct location. I have also made sure to place my app.use(errorHandler) at the bottom of my code to ensure it is the last error handler to run.
As of now when I encounter an error, my application crashed completely and does not return a json response with a stack trace.
This is going to cause issues later when trying to send errors back to the client side.
I can always go back to manually defining them through res.status(statusCode).json({message}) but this is the "node" way of doing things and does not implement the express syntax of "throw new Error(message)"
Any ideas on how to get a json response out of this without the app crashing while still using custom error handling and express throw new Error?
Thank you so much for the time, I appreciate it!
I just went through a tutorial and made a simple restful api. Afterwards, I added an admin user to my database. When I run the server, the authentication works, but if I try to access data via the api from the browser I get the eternal "waiting for response from localhost". I'm not sure what else I need to do for the request to be processed now that I've included a user authentication in mongodb.
var MongoClient = require('mongodb').MongoClient;
MongoClient.connect("mongodb://admin:password#localhost/bookstore?authSource=admin");
app.get("/", function(req, res){
res.send("Hello World");
});
When going to localhost:3000/ I see hello world, but localhost:3000/api/books won't return anything
app.get("/api/books", function(req, res){
Genre.getBooks(function(err, books){
if(err){
throw err;
}
res.json(books);
});
});
Here's the getBooks function
module.exports.getBooks = function(callback, limit){
Book.find(callback).limit(limit);
};
You're not in any way handling the callbacks from Node correctly. You also haven't defined what Genre or Book is in your answer.
That said, a couple notes. First, if you are in a callback, you never want to throw an error (as in don't use the syntax throw err), instead you want to either handle it or pass it back to your calling code.
A common approach I use would be like this:
app.get("/api/books", function(req, res, next){ // note adding next here
Genre.getBooks(function(err, books){
if(err){
return next(err);
}
return res.json(books);
});
});
// sometime later, a generic error middleware. You can make yours more useful
app.use((err, req, res, next) => {
return res.status(500).send({ message: 'An error has occurred' });
});
Your mongoclient connection and getBooks syntax are also probably wrong, but I can't advise you the best way to fix it without knowing if you're using Mongoose or if you're doing something else.
Ok, so since you're using Mongoose, you should remove the MongoClient code you have. Instead, add at app startup:
const mongoose = require('mongoose');
mongoose.connect('your connectionstring here');
and your getBooks should look like this:
module.exports.getBooks = function(callback, limit){
Book.find({}).limit(limit).exec(callback);
};
As a sidenote, node apps typically use the callback as the final argument in a parameter list, so you might want to change the function signature to (limit, callback) and adjust the calling code accordingly.
I figured out the problem. I didn't have mongoose properly set as the client, so when referencing the exported code for finding the book data nothing happened.
So I'm querying the Blizzard API Battle.Net for some information, character name and the realm they're in. Ofcourse it's possible for a user to query for a character that Does Not Exist, so Blizzard throws a 404 back to me and my server.js file doesn't know what to do with it even though I put something in place to handle it.
Releveant server.js code:
var express = require('express');
var app = express();
var fs = require('fs');
var bodyParser = require('body-parser');
var jsonParser = bodyParser.json();
const blizzard = require('blizzard.js').initialize({apikey: "dummy"});
app.use(express.static(__dirname + '/Source'));
//Listen on port 3000
app.listen(3000, function() {
console.log("Launch successful. To access app, open your browser and insert the following URL into your address bar: http://localhost:3000/");
});
app.post('/buttonpress', jsonParser, function (req, res) {
blizzard.wow.character(['profile'], { origin: 'us', realm: req.body.realm.name, name: req.body.name })
.then(response => {
if(response.status != 200){
res.send("That character doesn't exist! Please enter a valid character name.");
} else {
console.log(response.data);
res.send(response.data);
}
});
});
I attempt to handle anything that's not a 200 by sending something to the client to tell the user: Character DNE!, but instead vscode gives me some red error codes mentioned in the title of this post (in vscode debugger anyway).
When I try this from a command line, just running node server.js, nothing happens when I click the Search Button. I've set breakpoints and it looks like the function doesn't get a response from the server. So the 404 is happening no matter what but I can't figure out how to handle it.
Try placing your app.listen below/after your app.post Express.js runs in a middleware functionality so your listen is blocking all preceding code.
I'm very new to AngualarJS and Node.js, and I have a problem with my $http.get method's success callback block not being executed. When the request fails, my error callback block works, but when it succeeds, nothing happens.
This is what I have in my server.js:
var express = require('express');
var app = express();
// This is for testing GET connection with server
app.get('/test', function (req, res) {
console.log("Got a GET request from my angular app");
res.send('Hello ANGULAR');
})
var server = app.listen(8081, function () {
var host = server.address().address
var port = server.address().port
console.log("Example app listening at http://%s:%s", host, port)
})
This is what I have in my app.js:
$http.get("http://localhost:8081/test")
.success (function (response) {
alert("GOT A RESPONSE");
})
.error (function() {
alert("SERVER GET TEST FAILED THO");
});
Does anyone know what I'm doing wrong?
Your server's app.get block is missing a response sending :
Try adding :
res.sendStatus(200);
or res.status(XXX).send(*object/string*);
at the end.
Here's the documentation : http://expressjs.com/en/4x/api.html#res
The angular and node.js code look fine. But there could be any number of problems between network, configuration, etc.
The best way to figure this out is to use Chrome Dev tools network panel. Click "Inspect" on your page or Command-ALT-i (Mac).
Click the "network" panel tab to see your request and the response received.
try this :
$http.get('http://localhost:8081/test').then(
function successCallback(response) {
console.log("ok")
},
function errorCallback(response) {
console.log("error") ;
}
);
According to the official documentation, don't use success :
Deprecation Notice
The $http legacy promise methods success and error have been deprecated. Use the standard then method instead. If $httpProvider.useLegacyPromiseExtensions is set to false then these methods will throw $http/legacy error.
Is it possible to wrap every request coming through express.js in a domain or trycatch see trycatch info here?
I am trying to create a 'catch all' of sorts (the express error handler middleware does not catch async calls) to be sure any errors I miss are handled with a 500 being sent to the user.
If you have an asynchronous function call (eg. process.nextTick()), then it will be outside the scope of express' error handler, thus killing the process entirely. Thus, using the express error handler will not work in all cases.
Express already has error handler implementation. It inherit it from connect. To use it you need to add it as the last middleware point (last app.use(...) call). For example:
var express = require('express')
, app = express();
app.use(app.router);
app.use(express.errorHandler());
// app.get(...), app.post(...), app.listen(...), etc.
If you want to handle all errors with simple 500 response code, you could replace express.errorHandler() with you own function. In that case your code will looks like:
var express = require('express')
, app = express();
app.use(app.router);
app.use(function(err, req, res, next){
if (!err) return next();
res.send(500);
});
// app.get(...), app.post(...), app.listen(...), etc.
More information about that way could be found in express error example comments in code
UPDATE:
Of course you could use domain for each request. You could wrap each request separately or use wrapping for router to handle ALL exceptions. Code is following:
var express = require('express')
, http = require('http')
, app = express()
, domain = require('domain');
//app.use(app.router);
app.use(function(req, res, next){
var d = domain.create();
d.on('error', function(er) {
console.log('error, but oh well', er.message);
res.send(500);
});
// explicitly add req and res
d.add(req);
d.add(res);
d.run(function() {
app.router(req, res, next);
});
});
app.get('/', function(req,res){
process.nextTick(function(){
throw new Error('Check Error');
});
});
http.createServer(app).listen(3000, function(){
console.log('Express server listening on port 3000');
});
!!BUT!! never use this in production. The reason of that is in nature how JS throw work. It will definitely be a cause of leaking in your application and make it even more unstable. You could use such error handling to implement custom algorithm of shutdown (for example to close already opened connection). More information about right use of domain could be found in documentation.
To monitor the leaking you could use the technique from this article.
UPDATE 2:
I just can't leave this not finished. trycatch code:
var express = require('express')
, http = require('http')
, app = express()
, domain = require('domain')
, trycatch = require('trycatch');
//app.use(app.router);
app.use(function(req, res, next){
trycatch(function(){
app.router(req, res, next);
}, function(er){
console.log(er.message);
res.send(500);
});
});
app.get('/', function(req,res){
process.nextTick(function(){
throw new Error('Check Error');
});
});
http.createServer(app).listen(3000, function(){
console.log('Express server listening on port 3000');
});
I had review the source of trycatch and there was no any magic. It still be cause of leaks. trycatch has domain under the hood.