How can I reduce this code duplication in NodeJS/Mongoose - node.js

I am using NodeJS and Mongoose for an application that has users. And I have a large number of actions the server does on a particular user, depending on the request.
That means, I have this particular code fragment appearing in a lot of functions:
User.findOne({'email':req.user.email}, function (err, user) {
if (err) {
console.log('err');
res.send('Error');
}
if(!user){
console.log('err');
res.send('Error');
}
// do something with returned user
user.data = ....
...
user.save(function(err) {
if(err) {
console.log('err');
res.send('Error');
}
else {
console.log('success');
res.send('Success');
}
}
As you can see, there is a lot of code that replicates. The code that changes is the part 'do something with returned user'. Almost everything else (error messages, etc.) remains same.
So, how can I extract this part out? Since this is working on callback mechanism, is there a certain way to achieve this?

One way is to use Promises. It would involve finding a way to convert the Mongooose api to return Promises instead of using callbacks. After that, you could create code that would follow the lines of
User.findOne(...)
.then((user) => {
// do something with the returned user
return user.save();
}).then(() => {
console.log('success');
res.send('Success');
}).catch(() => {
console.log('err');
res.send('Error');
});
Promises resemble traditional synchronous coding where you can propagate errors akin to try-catch block and thus needing only one error handling location. This way you wouldn't have to replicate the console.log('err'); res.send('Error'); lines in multiple places.
You can read an introduction to Promises for example in "Promises - A Gentle Introduction". For the part on converting Mongoose to Promises there may be an existing module for this, or another approach that APIs use is to not give callback function as the last argument and then a Promise is returned instead. Unfortunately, I don't have exact knowledge in this particular Mongoose API.

Related

How do I add a custom passport callback for every passport.authenticate function?

I am currently working on an account linking system, and I'm having trouble adding a custom callback function and url, I need atleast one of these to work. Please help.
The callback function doesn't run when I authorize my request, I've literally went to 100 threads about this, none of the seemed to fix this issue for me. My code is:
router.get("/link", async(req,res, next) => {
console.log("1/2")
passport.authenticate("discord", {}, function(err, user, info) {
console.log("2/2")
if (err) { return next(err); }
if (!user) { return res.redirect('/login'); }
})(req, res)
console.log("test")
})
Only the "1/2" and "test" logs, the "2/2" doesn't log. Which means that the function doesn't run.
And also, is there a way to change the callback url when you use the function, for example in the options i set {callbackUrl:"..."} or something similar, as that would be great too, I haven't been able to find any resource about this.

How to do good async?

I'm a PHP developer and currently i'm working on a node.js project, i've never experienced async before so it confusing me.
Am I really have to do it like this?
// user model
exports.getRandomUser = function(cb){
db.query('...query...', cb);
}
// post model
exports.getRandomPost = function(uid, cb){
db.query('...query...', cb);
}
// router
router.get('/', function(req, res) {
user.getRandomUser(function(userInfo){
post.getRandomPost(userInfo.id, function(postInfo){
res.render('post', {data: postInfo});
});
});
});
Is there any way to make it less confusing?
Great question, yes there is a more simplified manner. What you are doing here is callback after callback, which makes the code look 'confusing'
Commonly referred to as callback hell
It looks pretty now for standards, but in time it will grow into a hungry programming power beast that needs your attention all the time.
For a javascript pro it's not really hard to deal with it, but if you want to have a more relaxed style at approaching callbacks; you can use promises. Promises are a way into the future for JS, but I think it's good to understand BOTH
a callback mostly has two parameters it looks like this:
dothis(function (error, data) {
if (error) {
throw new Error(error)
}
console.log('we have data', data)
})
With promises, this becomes much more easy to understand in the semantic terms
dothis.then(function(data) {
console.log('we have data', data)
}).catch(function(error) {
throw new Error(error)
})
This of course only works if your functions are promise compatible, if you want to learn more about promises check out this tutorial github: https://github.com/then/promise
You can even chain promises and create a really clean codebase!

how to authenticate valid user in nodejs and mongo DB

I have a problem in validating user name and password in nodejs and mongodb . Below is the code what i tried. Password validation not working. what i am doing wrong here...
server.get('/login', function(req, res)
{
var status=""
user.findOne({name: req.query.username}, function(err, users)
{
if( err || !users)
{
console.log("No users found");
status="failed"
}
else
{
if(password==req.query.password)
{
status="success"
}
else
{
status="failed"
}
}
});
res.send(status);
});
Node.js runs on an asynchronous event loop, which means that your code won't necessary run the way you may always think it will (synchronously). To overcome this issue, there are a couple options: callbacks, or promises. Promises are generally the preferred route, and there are a couple libraries which I suggest: bluebird (is awesome) or async (which I haven't used a lot, but many prefer it).
If you'd like to work without promises for right now, you could fix your issue this way:
user.findOne({name: req.query.username}, function(err, users) {
if( !err && users && password === req.query.password ) {
res.send({ status: 'success' });
}
else { res.send({ status: 'failed' }); }
});
Again, the reason your code is not working is because the event loop reaches your res.send(status) before the database query has been resolved. My suggestion should work fine for you, but as your processes become more complex, I would look into promises to resolve any further async issues.

Error handling in Mongoose

I'm creating an API using Restify and Mongoose, and I'm completely new to both. I can't seem to figure out the proper way to handle errors in Mongoose / Node.
As of now, I'm trying to do something like this:
Submission.findById(req.params.submission_id, function(err, data) {
if (err)
return next(err);
res.send(data);
});
I'm attempting to call a GET on this (for a user that not exist). And rather than sending back a simple error message, it causes my entire node application to fail. I'm a bit confused on the user of return next(err) and what that exactly should do.
Any help is greatly appreciated.
A findById query that doesn't find a match isn't an error at the Mongoose level, so you if you want it treated that way you have to do it yourself:
Submission.findById(req.params.submission_id, function(err, data) {
if (err)
return next(err);
else if (!data)
return next(new Error("User not found"));
res.send(data);
});

Proper Handling of fetch errors for Mongoose?

This is a pure best practice question. I am pretty new to Node and Mongoose. I absolutely love the technology and have been cranking away on a project to build a JSON-backed API for an app that I'm building.
I am finding that I am continuously repeating code when I fetch objects from my database. For example:
Playlist.findById(req.params.id, function(err,playlist){
if (err)
return res.json({error: "Error fetching playlist"});
else if (!playlist)
return res.json({error: "Error finding the playlist"});
//Actual code being performed on the playlist that I'm fetching
});
The error handling at the top of the function call is annoying because I have to repeat that code for every call to the database... or so I think.
I thought about using a callback like:
var fetchCallback = function(err,objOrDoc,callback){
//Handle the error messages
callback(objOrDoc);
};
However, this approach would mess up my sequential flow since I would have to define the callback function before I performed the fetch. So, if I had a lot of database queries chained together, I would have to place the callbacks in reverse order, which is far from ideal in a clean-coding perspective.
I'm wondering if anyone has run into this issue and has any best practices for cutting down on the repetition.
I'm also using the express framework, so if there's a helpful way to handle it in express, I'd be interested to know, too.
There are a couple interesting approaches you could try here.
At the most simple, you could simply have a function that loads up an object and handles the output in an error condition.
fetchResource = function(model, req, res, callback) {
model.findById(req.params.id, function(err, resource) {
if (err)
return res.json({error: "Error fetching " + model.toString()});
else if (!playlist)
return res.json({error: "Error finding the " + model.toString()});
callback(resource);
});
};
app.on('/playlists/1', function(req, res) {
fetchResource(Playlist, req, res, function(playlist) {
// code to deal with playlist.
});
});
That's still quite a bit of duplication, so I might try to move this out into a middleware.
Route Middleware
Routes may utilize route-specific middleware by passing one or more additional callbacks (or arrays) to the method. This feature is extremely useful for restricting access, loading data used by the route etc.
Now I haven't tested this and it's a bit hand-wavey (read: pseudocode), but I think it should serve as a decent example.
// assuming a URL of '/playlist/5' or '/user/10/edit', etc.
function loadResource(model) {
return function(req, res, next) {
model.findById(req.params.id, function(err, resource) {
if (err)
return res.json({error: "Error fetching " + model.toString()});
else if (!resource)
return res.json({error: "Error finding the " + model.toString()});
req.resource = resource;
next();
});
}
}
app.get('/playlist/:id', loadResource(Playlist), function(req, res) {
var playlist = req.resource;
...
});
app.get('/user/:id', loadResource(User), function(req, res) {
var user = req.resource;
...
});
The express source contains a pretty good example of this pattern, and the middleware section in the docs (specifically under 'Route Middleware') details it further.

Resources