I have the following function in one of my mongoose models:
UserSchema.methods.checkUsernameExists = function checkUsernameExists(req){
User.count({ username: req.body.username }, function(err, count){
if(err){
return console.error(err);
}
console.log(count); //Logs 2
});
}
Now when I log it, it gives the correct count. But if I return the count and try doing this, in one of my controllers it returns undefined.
var User = require("../models/user").User;
var user = new User();
exports.signup = function(req, res){
var count = user.checkUsernameExists(req);
console.log(test)
}
Any help would be appreciated thank you.
Remember that Node works asynchronously, meaning that you can't return a normal value from a function that executes an asynchronous function itself, like your checkUsernameExists does.
The most common way to deal with this is by passing a callback function which is called when the value is retrieved:
UserSchema.methods.checkUsernameExists = function checkUsernameExists(req, callback) {
User.count({ username: req.body.username }, callback);
};
This will pass the err and count variables that are the result of User.count as arguments to the callback function you supply. To use:
user.checkUsernameExists(req, function(err, count) {
if (err) {
console.error(err);
} else {
console.log('the count is', count);
}
});
To make the function to what it's name suggests, namely to 'return' a boolean to signify if a username already exists, you might use something like this:
UserSchema.methods.checkUsernameExists = function checkUsernameExists(req, callback) {
User.count({ username: req.body.username }, function(err, count) {
if (err) {
callback(err);
} else {
callback(null, count !== 0);
}
});
};
Related
I have this function which passes a parameter
fetchprofile() {
let uri = "http://localhost:4000/data/username/" + this.tintin;
this.axios.post(uri).then(response => {
this.fname = response.data.fname;
this.lname = response.data.lname;
});
}
And this is the route:
adnmastroutes.route('/username/:usertin').post(function (req, res) {
console.log('TIN No. of user is:' + req.params.usertin);
adnmastmainmodel.findOne({tin: req.params.usertin}, function (err, user) {
if (err) {
console.log(err);
}
else {
console.log(user);
res.json(user);
} }); });
Now in the line
console.log('TIN No. of user is:' + req.params.usertin);
the passed parameter req.params.usertin has a value.
BUT WHY IS IT IN THE LINE
adnmastmainmodel.findOne({tin: req.params.usertin}, function (err, user) {
the passed parameter req.params.usertin ha NO value.
(the line:
adnmastmainmodel.findOne({tin: req.params.usertin}, function (err, user) {
returns a null response. But when I changed it this way:
adnmastmainmodel.findOne({tin: '012349876'}, function (err, user) {
it returns the desired response.)
PLEASE HELP HOW WILL I RESOLVE THIS?
p.s. The value of the parameter this.tintin came from sessionStorage.getItem
Make sure parameter is string by using the toString() function
adnmastmainmodel.findOne({tin: req.params.usertin.toString() }, function (err, user)
I have an async waterfall Array where the function otherIngrLists() is the 3rd to be executed. Every function before that worked fine.
function otherIngrLists(userslist, callback){
collection = db.get('ingrList');
collection.find({"userid":{$ne:userid}},{},function(err,docs){
if(!err){
var otherLists = docs;
var otherListsCount = docs.count();
console.log(otherListsCount);
callback(null, otherLists, otherListsCount, userslist);
} else {
callback(err, null);
}
});
},
The Problem is that this function is called twice. I assured this with a simple console.log().
How did I manage to call this function again? Did I get the concept of callbacks wrong as I use them to be passed on to the next function?
Also after this function executing twice an error ist thrown. It has nothing to to with this problem though and I will concern my self with that later.
Thank you for your time!
Waterfall Array in router.get:
router.get('/:userid', function(req, res) {
var db = req.db;
var collection;
var userid = req.params.userid;
async.waterfall(
[
function getIngrList(callback, userid) {
var route = 'http://localhost:3000/users/zutatenliste/'+userid;
request(route, function(err, response, body){
if (!err && response.statusCode === 200) {
var userlist = body;
callback(null, userlist);
} else {
callback(err, null);
return;
}
});
},
function otherIngrLists(userlist, callback){
collection = db.get('zutatenListe');
console.log(userid);
collection.find({"userid":{$ne:userid}},{},function(err,docs){
if(!err){
var otherLists = docs;
var otherListsCount = docs.count();
callback(null, otherLists, otherListsCount, userlist);
} else {
callback(err, null);
}
});
},
function pushInArray(otherLists, otherListsCount, userlist, callback){
console.log("test");
...
...}
}
}
Edit 1: --Also either if cases are executed, first the true one then the false--
// Does not happen anymore
Edit 2: Added the whole Thing until the problematic function
Please provide some Additional details as this function seems perfect and No, You haven't misunderstood the concept of callback you are using it correctly.
Structure of Async Waterfall
var create = function (req, res) {
async.waterfall([
_function1(req),
_function2,
_function3
], function (error, success) {
if (error) { alert('Something is wrong!'); }
return alert('Done!');
});
};
function _function1 (req) {
return function (callback) {
var something = req.body;
callback (null, something);
}
}
function _function2 (something, callback) {
return function (callback) {
var somethingelse = function () { // do something here };
callback (err, somethingelse);
}
}
function _function3 (something, callback) {
return function (callback) {
var somethingmore = function () { // do something here };
callback (err, somethingmore);
}
}
so, in waterfall you can pass the values to the next function and your 3rd function is correct.
Edited
async.waterfall(
[
//can not give userId as second parameter
function getIngrList(callback) {
//if you want userId you can pass as I shown above or directly use here if it's accessible
var route = 'http://localhost:3000/users/zutatenliste/'+userid;
request(route, function(err, response, body){
if (!err && response.statusCode === 200) {
var userlist = body;
callback(null, userlist);
} else {
callback(err, null);
// return; no need
}
});
},
function otherIngrLists(userlist, callback){
collection = db.get('zutatenListe');
console.log(userid);
collection.find({"userid":{$ne:userid}},{},function(err,docs){
if(!err){
var otherLists = docs;
var otherListsCount = docs.count();
callback(null, otherLists, otherListsCount, userlist);
} else {
callback(err, null);
}
});
},
function pushInArray(otherLists, otherListsCount, userlist, callback){
console.log("test");
...
...}
As said you can not pass userId as last parameter over there. Let me know if you still get the same error.
First you need to declare you function:
function myFuntion(userId, callback) {
async.waterfall([
function(callback) {
//do some thing here
callback(null, userlist);
}, function(userId, callback) {
//do something here
callback(null, orderList, orderListCount, userlist);
}
], function(err, orderList, orderListCount, userlist) {
if(err)
console.log(err);
else
callback(orderList, orderList, userlist);
})
}
After that you can use function:
myFuntion(userId, function(orderList, orderListCount, userlist) {
console.log(orderList);
console.log(orderListCount);
console.log(userlist);
})
I am new to NodeJS. I know there are a lot of questions about asynchronous NodeJS but I couldn't find exactly what I am looking for.
My problem is:
I want to check if username and email already exist or not in my database. Two separate functions for username and email. Another function is for storing data to database.
I don't know how to do this using asynchronous NodeJS pattern.
User.js (mongoose Schema)
const mongoose = require('mongoose');
var userSchema = mongoose.Schema({
name: String,
username: { type: String, required: true, unique: true },
password: { type: String, required: true },
email: { type: String, required: true, unique: true},
aiub_id: String,
});
const Users = module.exports = mongoose.model('User', userSchema);
module.exports.addUser = function (user, callback) {
user.save(callback);
}
module.exports.usernameExist = function (givenUsername, callback) {
Users.find({ username: givenUsername }, callback);
}
module.exports.emailExist = function (givenEmail, callback) {
Users.find({ username: givenEmail}, callback);
}
index.js (route)
route.post('/signup', function(req, res){
// GRAB USER INFO FROM HTML FORM
var newUser = new User({
name : req.body.tfullName,
username : req.body.tusername,
password : req.body.tpassword,
email : req.body.temail,
aiub_id : req.body.tuserID
});
// This block send 200 if username doesn't exist
User.usernameExist(newUser.username, function (err, result){
if(err){
throw err;
}
if(result.length <= 0){
res.send({status : 200 });
}else{
res.send({status : 100 });
}
});
});
Please me help to solve this and please forgive if it sounds stupid.
Use Promises.
First I suggest you change your functions to return promises instead of taking callbacks:
function userExists(parameters) {
return new Promise((resolve, reject) =>
Users.find(parameters, (err, result) => {
if(err){
reject(err);
} else {
resolve(result.length <= 0);
}
})
);
}
module.exports.usernameExist = function (givenUsername) {
return userExists({ username: givenUsername });
}
module.exports.emailExist = function (givenEmail) {
return userExists({ email: givenEmail });
}
Then you wrap the promises returned from parallell calls to these functions in a Promise.all, which returns a new promise that resolves when all the wrapped promises get resolved, and then you do your stuff there:
Promise.all([
User.usernameExist(newUser.username),
User.emailExist(newUser.email)
]).then((results) => {
// results[0] contains the result from User.usernameExist
// results[1] contains the result from User.emailExist
});
If you do not want to change your functions in the User module, you can wrap the calls to those functions in Promises in your index.js file instead.
Just call one function in callback of other,
// Check if username doesn't exist
User.usernameExist(newUser.username, function (err, result){
if(err){
throw err;
}
if(result.length <= 0){
//check if email doesn't exist
User.emailExist (newUser.email, function(err, result){
if(result.length<=0){
//save user
User.addUser(newUser, (err, result)=>{
if(!err){
res.send({status : 200 });
}else{
res.send({status : 100 });
}
})
}
}
}
});
People prefer promises to callback, to use promise you'll have to return a promise from your function. With promises code is easier to read so look into that.
I have a module User - like this:
module.exports = User = (function() {
function User(params) {
this.id = params.id;
this.first_name = params.first_name || '';
this.last_name = params.last_name || '';
this.username = params.username;
this.email = params.email;
this.password = params.password;
};
User.findByUsername = function(username, callback) {
if (!_.isEmpty(username)) {
var opts = {table: TABLE, where: {sql: "username='"+username+"'"}};
QueryDispatcher.findWhere(opts, function(err, result) {
if(!_.isEmpty(err)) { return callback(err, null)}
callback(null, result.rows[0]);
});
};
};
return User;
};
The function that uses the class method:
module.exports = AuthStrategies = (function() {
AuthStrategies.localStrategy = function(username, password, done) {
async.waterfall([
function(callback) {
User.findByUsername(username, function(err, user){
if (err) { callback(err) };
if (_.isEmpty(user)) {
callback(null, false, { message: 'Incorrect username.' });
};
callback(null, user, null)
});
},
function(user, opts, callback) {
"do something here and call the next callback"
}]
, function(err, user, opts) {
if(err) { return done(err)}
if(!user) { return done(null, false, opts.message)}
done(null, user)
});
};
return AuthStrategies;
})();
I have my jasmine test -
var Auth = require('path to AuthStrategies module')
describe('Auth', function() {
describe('#AuthStrategies.localStrategy', function() {
describe('when user creds are valid', function() {
var test_user;
beforeEach(function(){
test_user = new User({
username: 'test996'
, password: 'password123'
, email: 'testemamil#email.com'
, first_name: ''
, last_name: ''
});
spyOn(User, "findByUsername").and.callFake(function(usrename, cb) {
cb(null, test_user);
});
});
it('returns user object', function(done) {
Auth.localStrategy('test996', 'password123', function(err, user, opts) {
expect(err).toEqual(null);
expect(user).toEqual(test_user);
done()
})
});
});
});
});
Essentially I want to stub out the User Class method findByUsername and fake the callback with my own results i.e nul error and a user(as if the find was successfully).
I have Spy on many "class" methods in my app and don't have this problem. This is baffling me a bit. The error only shows when I add .and.callThrough or .and.callFake to the spy.. the moment I remove this the test just times out ...which makes sense as the spy works and doesn't call the callback needed for the async waterfall to continue.
The error I am getting is -
So I figured it out -
The error you see above happens anyway. The reason I was getting the above "extra info" which was throwing me off btw - Was because I was running the test separately.
./node_modules/.bin/jasmine ./tests_to_run_spec.js
What would normal happen - is I would get a timeout failure due to a missing callback. as in my case above I wasn't calling the callback in the faked function I sup[plied properly.
actually even more interestingly - running jasmine-node from my PATH doesn't like the .and.... being called on this particular spy. Really have no idea. but that how I got the spyOn(User, 'findByUsername').and.callFake ... to work
I am querying my mongodb for the user's email that is being passed through a session. When that email is found it looks for that user's friends, and those friends are supposed to be passed to the usersFriends array which is then sent in the chunk to the browser. I included all the code in this block even though the transaction block isn't really pertinent or so I think.
The Problem: is that the usersFriends array is outputting an empty array everywhere except when the console.log is inside the for loop. Thoughts?
app.get('/api/chunk', function(req, res){
var last5;
var usersFriends = [];
Transaction.find().sort({$natural:-1}).limit(5).exec(function(err, docs){
if (err) {
console.log(err);
} else {
last5 = docs;
}
});
User.findOne({ email: req.user.email }, function(err, user){
if (!user) {
console.log(err);
} else {
for (var i = 0; i < user.friends.length; i++) {
(function(cntr){
User.findOne({ email: user.friends[cntr].email}, function(err, result) {
result.password = "Sneaky sneaky"
var name = result.firstName + " " + result.lastName;
usersFriends.push({
name: name,
index: cntr
});
});
})(i);
var chunk = {
"friends": usersFriends,
"transactions": last5
};
} }console.log(usersFriends); // empty array
});
});
Combining all the various things we've discussed in comments for this list of changes:
Pass the cntr in an IIFE so it is uniquely captured for each separate .findOne() request.
In each response, check if this is the last one done so we know that all results have arrived.
Start the .findOne() operations from the completion of the Transaction.find() operation (since I don't know that operation, I'm guessing how this particular aspect should be implemented, but you should be able to see the general idea).
Though would result in this code:
app.get('/api/chunk', function(req, res) {
var last5;
var usersFriends = [];
Transaction.find().sort({
$natural: -1
}).limit(5).exec(function(err, docs) {
if (err) {
console.log(err);
} else {
last5 = docs;
User.findOne({
email: req.user.email
}, function(err, user) {
if (!user) {
console.log(err);
} else {
var totalCnt = user.friends.length;
for (var i = 0; i < totalCnt; i++) {
(function(cntr) {
User.findOne({
email: user.friends[cntr].email
}, function(err, result) {
result.password = "Sneaky sneaky"
var name = result.firstName + " " + result.lastName;
usersFriends.push({
name: name,
index: cntr
});
if (usersFriends.length === totalCnt) {
// all results are done here
// you can create the final response
var chunk = {
"friends": usersFriends,
"transactions": last5
};
}
});
})(i);
}
}
});
}
});
});