node.js + Express - How to properly throw/catch errors - javascript

I just started a project using Express and I have no real knowledge in node.js, and even in Javascript in general. I was wondering what was the proper way of throwing and catching errors so that they display correctly, and also so that I don't duplicate too much code. I thought this bit of code in app.js:
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handlers
// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: err
});
});
}
// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: {}
});
});
acted like a big catch that would intercept all the errors I would throw in my controllers. But I don't really know how to make it work. For example, let's take this bit of code in users.js:
/* GET users listing. */
router.get('/', function(req, res, next) {
connection.query('SELECT id, name FROM user', function(err, rows, fields) {
if (err) throw err;
res.render('users', {
title: 'Users',
userList: rows
});
});
});
How do I send something like a 204 code (No Content)
How do I report an error with the database
Any help/hint would be appreciated, I'll keep looking on forums and stuff in the meantime.

Express uses a series of middleware to handle the request. If the route matches and defined route it will handle it. Otherwise, you can put '/404' route at last after importing all the routes. It will automatically reach there when no route is found. Else if you don't want to send a response you can use next() so that it moves to next middleware and ultimately to '/404' middleware.
/* GET users listing. */
router.get('/', function(req, res, next) {
connection.query('SELECT id, name FROM user', function(err, rows, fields) {
if (err) {
return res.status(502).json({message:'db error'});
};
res.render('users', {
title: 'Users',
userList: rows
});
});
});

Thank you all for your answers. What I was looking for (and found) was the next(error) function to pass control to the error handler located in app.js (Cf. http://expressjs.com/en/guide/routing.html for more details).

You can give a relevant message with statusCode like this
/* GET users listing. */
router.get('/', function(req, res, next) {
connection.query('SELECT id, name FROM user', function(err, rows, fields) {
if (err){
res.status(500).send("Something is not right");
};
//if you get rows of array type, then you can do this for empty records
else if(rows.length===0){
res.status(204).send('No records found!')
}else{
res.render('users', {
title: 'Users',
userList: rows
});
}
});
});

In Express, each route handler is passed a 'res' parameter, that is used to provide the response to the client. Sample usage of creating a response with a specific code, code error and empty body
router.get('/', function(req, res, next) {
res.status(401, "Authentication mismatch").json({});
});
Instead of json() you can also use send() to send a plain text back.

Related

Express Error Handling: Sending a Message to Frontend

I'm having some trouble error handling my authentication API calls. When I send the 500 status from Express, my frontend (Vue in this case) only picks up the message Request failed with status code 500 rather than something more helpful for triage like this is the worst error ever (in the example below).
In the below example, when I call '/post' from the API, I throw an error which is handled by my custom middleware. The middleware successfully handles the error and sends the appropriate status to my frontend, but I can't figure out how to send useful messages (e.g. 'this is the worst error ever') / access them in the front end.
Is this a common use case? Am I doing anything obviously wrong? Does the message I send come up in the (err) parameter, or do I need to add a resp parameter?
Express Route for '/login'
router.post('/login', (req, res, next) => {
throw Error('this is the worst error ever')
})
Custom express error handler
app.use(function(err, req, res, next) {
res.status(err.status || 500).send({
error: {
status: err.status || 500,
message: err.message || 'Internal Server Error',
},
});
});
Handle the API Response in Vue
login (e) {
e.preventDefault();
UserService.login(this.username, this.password) //this is simple axios post call
.catch((err) => {
this.loginStatus = err.message
return
})
}
Found the answer to this for those that find this helpful. The err that is caught has a response variable. This is where the data is sent via the express send command. See corrected code below for the frontend:
login (e) {
e.preventDefault();
UserService.login(this.username, this.password) //this is simple axios post call
.catch((err) => {
this.loginStatus = err.response.data.message
return
})
}
I think you need to add:
Throw new Error()
instead of
Throw Error
If you are making an asynchronous calling you can do this
app.get('/', function (req, res, next) {
fs.readFile('/file-does-not-exist', function (err, data) {
if (err) {
next(err) // Pass errors to Express.
} else {
res.send(data)
}
})
})
Code should look like this:
router.post('/login', (req, res, next) => {
throw new Error('this is the worst error ever')
})
Check this in express documentation

How to compare and validate the requested URL with a custom URL string?

I have a route that goes to the index page. I have a secret token that allows access to this page. I want to compare the requested URL with a custom string. The current URL in use is http://localhost:3000/?token=secret but if I enter http://localhost:3000/as?token=secret it doesn't render the 404 error page that I created, instead says Cannot GET /as. I wondering how to validate this and render out the error page correctly
app.get('/', (req, res) => {
console.log(req.url); // /?token=secret
if (req.url !== `/?token=${websocket_token}`) {
res.render('error', {
title: '404 Not Found',
errorMessage: '404 Not Found'
});
return;
}
});
In Express each app.get or other related method handles it's own route. So when you do app.get('/' you are only matching routes that are / not /as.
You could change it to * to match all routes. Maybe like the following?
app.get('*', (req, res) => {
console.log(req.url); // /?token=secret
if (req.url !== `/?token=${websocket_token}`) {
res.render('error', {
title: '404 Not Found',
errorMessage: '404 Not Found'
});
return;
}
});
Or of course you could have a dedicated section for your 404 messages.
app.get('/', (req, res, next) => {
console.log(req.url); // /?token=secret
if (req.url !== `/?token=${websocket_token}`) {
return next();
}
// Valid request
});
app.get('*', (req, res) => {
res.render('error', {
title: '404 Not Found',
errorMessage: '404 Not Found'
});
});
At the end of the day there are so many ways you can handle Express routing. It's very powerful and flexible.
I'd suggest you look here under the How do I handle 404 responses? section for another idea as well.
Also, remember, having secrets in a URL like that, probably isn't the most secure thing. So there are a lot of reasons I wouldn't suggest this for security reasons. But just answering your question, the above should work.
The idiomatic way to handle 404s in Express is to register your final route handler with the use method rather than using one of the HTTP-specific methods.
app.use((req, res) => {
res.render('error', {
title: '404 not found',
errorMessage: '404 not found'
})
})
I emphasize the word final because use registers a catch-all handler, so this will override any route that it precedes in your code. If all your other routes are registered before this, then this will catch any request that has not matched any other route – regardless of the HTTP method that was used. So this will apply to any GET, POST, PUT, DELETE request.
An even more idiomatic way in Express to handle 404s (and all HTTP error responses) is to use the next argument that comes with all route handlers. This will re-route the request to the next handler that specifically takes an error as it's first argument:
app.use((req, res, next) => {
const error = new Error('404 not found')
error.statusCode = 404
next(error)
})
app.use((error, req, res, next) => {
res.status(error.status || 500)
res.render('error', {
title: error.message,
errorMessage: error.message
})
})
This is great because you now have a generic error handler, which you can access from inside any other route. So this will handle not only 404s, but also 401s, 403s, 503s, anything you want that doesn't render successfully for the user. And you can access this route simply by calling next with an error as the first argument from inside any other route handler.
I suggest you to use passport-auth-token for validating the token & display success or error pages.
Configure Strategy
The token authentication strategy authenticates users using a token. The strategy requires a verify callback, which accepts these credentials and calls done providing a user.
passport.use('authtoken', new AuthTokenStrategy(
function(token, done) {
AccessToken.findOne({
id: token
}, function(error, accessToken) {
if (error) {
return done(error);
}
if (accessToken) {
if (!token.isValid(accessToken)) {
return done(null, false);
}
User.findOne({
id: accessToken.userId
}, function(error, user) {
if (error) {
return done(error);
}
if (!user) {
return done(null, false);
}
return done(null, user);
});
} else {
return done(null);
}
});
}
));
Authenticate Requests
Use passport.authenticate(), specifying the 'authtoken' strategy, to authenticate requests.
For example, as route middleware in an Express application:
app.post('/login',
passport.authenticate(
'authtoken',
{
session: false,
optional: false
}
),
function(req, res) {
res.redirect('/');
}
);

Not able to return proper response after insert

I'm using Node.js/Express.js to install data to my MySQL DB.
Inserting data works fine, but returning success / fail gives me an error.
TypeError: Cannot read property 'status' of undefined
This is my code:
var crud = {
newProject: function (req, res, callback) {
db.query('INSERT INTO projects SET ?', req.body, function(err, res) {
// This is where it fails
if(err){
return res.status(500).json({error: err});
} else {
return res.status(200).json({success: 'Insert row success'});
}
});
},
}
// Express routing
app.post('/project/*', crud.newProject);
What am I not getting right here?
Solution
So this is what I used to make it work (after changing 'res' to 'resp' as suggested):
if (err) throw err;
res.end(JSON.stringify({response: 'Success'}));
Your defining res twice. The express response object is getting overwritten by the data param in your node callback.
Try the following (see comment)
var crud = {
newProject: function (req, res, callback) {
// changed 'res' to 'resp' to avoid collision with Express' 'res' object
db.query('INSERT INTO projects SET ?', req.body, function(err, resp) { // here's your error
// This is where it fails
if(err){
return res.status(500).json({error: err});
} else {
return res.status(200).json({success: 'Insert row success'});
}
});
},
}
// Express routing
app.post('/project/*', crud.newProject);
If you define error-handling middleware functions after the last app.use() in your main configuration
app.use(function (err, req, res, next) {
res.status(500).send(err.message || 'Internal server error.')
})
You can use the next callback as a catchall error handler, so the above would then become
var crud = {
newProject: function (req, res, callback) {
db.query('INSERT INTO projects SET ?', req.body, function(err, resp) {
if (err) return callback(err);
return res.json({success: 'Insert row success'});
});
},
}
// Express routing
app.post('/project/*', crud.newProject);
res.json() by default should add a 200 Success code to the response header. Ideally you would want to inspect the resp data param from the node callback after checking the state of err to properly handle the response and proceed accordingly, especially if you are dealing with last evaluated records associated with a continuation token usually provided in the response which some DBALs and APIs do for you and some don't. Either way you will want to be sure additional recursion isn't necessary to fetch remaining records before responding successfully.
Looks like the res object is undefined as it is not returning any response after the insert. You may return a new object like:
return {
status: 200,
json: {success: 'Insert row success'}
}

Find and render user from mongodb

A little background. I am sending a post with the username. About what he came to me is displayed in the log.
console.log(req.body.username); // 'username'
How do I use mongodb to find and render a user with a username from the post?
For example, the sample and render of all users looks like this, but I still do not understand how to get the one I need
exports.profile = function(req, res, next){
User.find(function(err,users){
if(err) return next(err);
res.render('users/profile',{ users:users });
});}
Try this
exports.profile = function(req, res, next){
User.find({username: req.body.username}function(err,users){
if(err) return next(err);
res.render('users/profile',{ users:users });
});}
and here you have some docs to read http://mongoosejs.com/docs/queries.html
Use findOne function to find.
User.findOne({username: req.body.username}.function(err,users){
if(err) return next(err);
res.render('users/profile',{ users:users });
});}

Why isn't Node.js Express with MongoDB responding to fetch API calls (ReactJS and Redux)?

Using ReactJS, Redux, Webpack, Node.js and Express with MongoDB, I am following the tutorial https://github.com/vasansr/mern-es6 and trying to integrate it into my project. First, I am trying to make a POST request to the server I created. And it gets a response with a success and no error is logged. Yet inside the server POST API, it does not log console.log('Req body', req.body);, and in terminal I checked to see if the database has been created with mongo -> show dbs but it is empty.
Could it be that something is intercepting the request from the server? What could be the issue?
This...
app.use('/', function (req, res) {
res.sendFile(path.resolve('client/index.html'));
});
comes before:
app.post('/api/users/', function(req, res) {
//...
});
Since it's app.use the POST /api/users will still hit that middleware, and res.sendFile ends the request/response. You'll probably see that your post is getting back the client HTML.
Try moving your client HTML endpoint to the end of your middleware, just before the error handlers if you have them. That way, it'll only get used if none of your API endpoints match. Or if you want just GET / to return the HTML, change use to get:
app.use(webpackDevMiddleware(compiler, {noInfo: true, publicPath: config.output.publicPath}));
app.use(webpackHotMiddleware(compiler));
app.use(express.static('dist')); //where bundle.js is
app.use(bodyParser.json());
app.post('/api/users/', function(req, res) {
console.log('Req body', req.body);
var newUser = req.body;
db.collection('users').insertOne(newUser, function(err, result) {
if(err) console.log(err);
var newId = result.insertedId;
db.collection('users').find({_id: newId}).next(function(err, doc) {
if(err) console.log(err);
res.json(doc);
});
});
});
app.get('/', function (req, res) {
res.sendFile(path.resolve('client/index.html'));
});
app.post('/api/users/', function(req, res) {
console.log('Req body', req.body);
var newUser = req.body;
db.collection('users').insertOne(newUser, function(err, result) {
if(err) console.log(err);
var newId = result.insertedId;
db.collection('users').find({_id: newId}).next(function(err, doc) {
if(err) console.log(err);
res.json(doc);
});
});
});
I have a small comments about this code, for if(err) console.log(err); i think you should change to if(err) return console.log(err);.
For error case, i think you need return, otherwise the below part will be excuted, and there will report some error.

Categories

Resources