I was trying to do a simple function with Node.js and Mongoose that returns true if the model is empty.
The mongoose configuration is fine:
var mongoose = require('mongoose');
var db = mongoose.createConnection( 'mongodb://localhost:27017/prueba' );
var userSchema = mongoose.Schema({
phoneNumber: Number,
name: String
});
var User = db.model('User', userSchema, 'User'');
Then I tried to do this:
User.find(function(err, data) {
if (err) {console.log(err)};
console.log(data.length == 0 );
});
And it works fine, it logs true, or false.
Then I tried to do:
var isUsersEmpty = function () {
User.find(function(err, data) {
if (err) {console.log(err)};
console.log(data.length == 0);
});
}
isUsersEmpty();
And again it works fine, it logs true or false, buy if I do:
var isUsersEmpty2 = function () {
User.find(function(err, data) {
if (err) {console.log(err)};
return data.length == 1;
});
}
console.log(isUsersEmpty2());
Then the log prints "Undefined".
What can I do if I need a function that returns true or false to do things like this:
if (isUsersEmpty2()) {} //Do something here...
And isUsersEmpty2() returns always undefined.
isUsersEmpty2() returns a promise , which means you can't just log it like you did. You need to send a response from the function. This should work:
var isUsersEmpty2 = function (res) {
User.find(function(err, data) {
if (err) res(err, null);
res(null, data.length == 1);
});
}
isUsersEmpty2(function(err, res) {
if(res) {/*do something*/}
});
Related
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 years ago.
I'm working in a simple API Key authentication, I just want to verify the given key against the user provied key.
I have a seperate file with the function querying the database, and returning true/false and the user object.
But in my route.js file, the return object is undefined even tough in my auth.js file it isn't.
I tried making the the function in router.get an async function using express-promise-router and making the function an await return var user = await auth.verify(req.params.uid, req.get("token")) but I don't realy know how async works.
router.js
[...]
router.get('/list/:uid', function(req, res) {
var user = auth.verify(req.params.uid, req.get("token"))
console.log("User: " + user) // <-- Undefined
if (user.status) {
res.send("Success")
} else {
res.status(403)
res.json({status: 403, error: "Unkown User / Token"})
}
})
[...]
auth.js
var db = require('./db')
var ObjectId = require('mongodb').ObjectId;
module.exports = {
verify: (uid, key) => {
try {
var collection = db.get().collection('users')
const obj_id = new ObjectId(uid)
const query = { _id: obj_id }
collection.find(query).limit(1).toArray(function(err, user) {
var status = 0;
var usr = {};
if (err) {throw err}else{status=1}
if (user.length <= 0) {throw "NotExistingExc"; status = 0}else{
usr = user[0];
if (key != usr.api) status = 0
}
var returnObj = {
status: status,
user: usr
} /* --> Is {
status: 1,
user: {
_id: d47a2b30b3d2770606942bf0,
name: 'Sh4dow',
groups: [ 0 ],
api: 'YWFiMDI1MGE4NjAyZTg0MWE3N2U0M2I1NzEzZGE1YjE='
}
}
*/
return returnObj;
})
} catch (e) {
console.error(e)
return {
status: 0,
user: {},
error: e
}
}
}
}
db.js (Idk if needed)
var MongoClient = require('mongodb').MongoClient
var state = {
db: null,
}
exports.connect = function(url, done) {
if (state.db) return done()
MongoClient.connect(url, { useNewUrlParser: true }, function(err, db) {
if (err) return done(err)
state.db = db
done()
})
}
exports.get = function() {
return state.db.db("database")
}
exports.close = function(done) {
if (state.db) {
state.db.close(function(err, result) {
state.db = null
state.mode = null
done(err)
})
}
}
I want to have the returnObjin auth.js in the router.get of my route.js file.
Make auth.verify return a Promise which we can then await for it inside router, You can just make the callback async no need for express-promise-router
router.get('/list/:uid', async function(req, res) {
try {
var user = await auth.verify(req.params.uid, req.get("token"))
console.log("User: " + user)
if (user.status) {
res.send("Success")
} else {
res.status(403).json({status: 403, error: "Unkown User / Token"})
}
} catch (e) {
console.error(e)
res.status(/* */).json(/* */)
}
})
auth
module.exports = {
verify: (uid, key) => new Promise((resolve, reject) => {
var collection = db.get().collection('users')
const obj_id = new ObjectId(uid)
const query = { _id: obj_id }
collection.find(query).limit(1).toArray(function(err, user) {
var status = 0;
var usr = {};
if (err) {
reject(err)
return
} else {
status = 1
}
if (user.length <= 0) {
reject(new Error("NotExistingExc"))
return
} else {
usr = user[0]
if (key != usr.api) status = 0
}
var returnObj = {
status: status,
user: usr
}
resolve(returnObj);
})
}
}
In short, the reason you get undefined is because the code in auth.js is asyncronous. But you're really close. The toArray method in MongoDB returns a promise, so you need to make sure you return that promise and then use it in the router correctly.
In auth.js, make sure verify returns a promise - just add return!
return collection.find(query).limit(1).toArray(...)
And then, change your usage of the verify to the async/await you originally tried:
router.get('/list/:uid', async function(req, res) {
var user = await auth.verify(req.params.uid, req.get("token"))
// More code here...
})
Sorry if the title is not quite descriptive.
I am using Node and trying to use export.module to have clean code.
app.js
// ...
require('./router')(app);
module.exports = app;
router.js
cloudant = require("./helpers/cloudant")
// ...
module.exports = (app) => {
// ...
app.post("/statsPage", function(req, res) {
// ...
var a = cloudant.listUsers();
console.log("from post ", a) // --> it shows ("undefined")
if(a == false || a == undefined ) {
res.render("error");
} else {
res.render("statsPage", {
results: a
});
}
cloudant.js
exports = module.exports = {}
exports.listUsers = function() {
db.find({selector: {_id:{ "$gt": 0}}}, function(err, body) {
if(err) {
console.log(err);
return false;
} else {
console.log(body.docs) // --> it shows results correctly
return body.docs;
}
});
}
I've made the same way others "export" methods, like "insert", so I'm convinced that this issue is not related neither to my db connection or export "config".
The db.find method is asynchronous, so the data you get from the database is only available in the callback function. If you look carefully at the function you're exporting in cloudant.js, you'll see that there is no return statement returning any data, only in the callback function, that doesn't help anything.
There are many ways to solve this (and many, many posts on SO dealing with it).
Simplest solution for you would be to pass your own callback to your listUsers function:
exports.listUsers = function (callback) {
db.find({ selector: { _id: { "$gt": 0 } } }, function (err, body) {
if (err) {
console.log(err);
callback(err);
} else {
callback(body.docs);
}
});
}
router.js
app.post("/statsPage", function(req, res) {
cloudant.listUsers(function (a) {
console.log("from post ", a);
});
});
I want to create using mongoose js a collection of kitten with this document in it {name: "mike"}.
After creating this document I want to print it's value.
I wrote this code below.
2 problems:
this code doesn't end (meaning when I wrote node file.js the cmd line stays open (stucked) and no return value is return (infinite loop like in a server).
the code doesn't print the value of "mike". just create this doucument...
what am I doing wrong?
thanks
var mongoose = require('mongoose');
var url = 'mongodb://Yotam:Yotam#ds023475.mlab.com:23475/small-talkz';
mongoose.connect(url);
var kittySchema = mongoose.Schema({
name: String
});
var Kitten = mongoose.model('kitten', kittySchema);
Kitten.create({ name: "mike" }, function (err, small) {
if (err) return handleError(err);
});
Kitten.findOne( { } ), function(err, docs){
console.log(docs.name);
};
return 1;
newKitten = { name: "mike" };
Kitten.create(newKitten, function (err, kitty) {
if {
(err) return handleError(err);
} else {
console.log(kitty); //OR console.log(kitty.name);
}
});
Kitten.findOne({name: "mike"}).exec(function(e, kitten) {
if (e) {
console.log(e)
} else {
console.log(kitten.name)
}
});
the problem was {for anyone whose intersted (and thanks for herkou)} that I did not use the exec command..
This works:
Kitten.findOne( { name: "mike"} ).exec( function(err, docs){
console.log(docs.name);
return;
});
update:
also had a probelm with race conditions... the create of the documnet not finished when the query was called. that is why I got undeinfed.
use this new code:
var mongoose = require('mongoose');
var url = 'mongodb://Yotam:Yotam#ds023475.mlab.com:23475/small-talkz';
mongoose.connect(url);
var kittySchema = mongoose.Schema({
name: String,
color:String
});
var Kitten = mongoose.model('Kitten', kittySchema);
var newKitten = { name: "mike", color:"white" };
Kitten.create(newKitten, function (err, kitty) {
if (err) {
return handleError(err);
} else {
call_query();
}
});
var call_query= function(){
var query= Kitten.findOne( { name: "mike"} );
query.exec( function(err, docs){
console.log(docs.color);
return;
});
}
return 1;
now I just need to understand why this script doesn't end.
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 have this code in node.js/express.js:
var User = mongoose.model('User');
var usersRouter = express.Router();
usersRouter.put('/:id', function(req, res) {
req.body._id = req.params.id;
var usr = new User(req.body);
usr.validate(function (err) {
if (err) {
res.status(400).json({});
return;
}
var upsertData = usr.toObject();
delete upsertData._id;
User.update({_id: usr._id}, upsertData, {upsert: true}, function(err) {
if (err) {
res.status(500).json({});
return;
}
res.status(204).json({});
});
});
});
It works fine, but I would like to send a different response to the client if a new document has been created (status 201 with json in response body) or an existing one has been updated (status 204).
Is there a way to tell the difference from the callback of User.update?
Use the third parameter from callback function:
...
User.update({_id: usr._id}, upsertData, {upsert: true}, function(err, num, n) {
if (err) {
res.status(500).json({});
return;
}
if (!n.updatedExisting) {
/* new document */
}
res.status(204).json({});
});
...
n is an object like this:
{ updatedExisting: false,
upserted: <ObjectId>,
n: 1,
connectionId: 11,
err: null,
ok: 1 }
updatedExisting property is true when a document was updated -- so it was created before. If it's false, then it means that the new document was created during this call.