I am little bit confused with my code it's not worked synchronusly as it's should be. I use everyauth to do authentication.
registerUser(function(newUserAttrs) {
var login = newUserAttrs[this.loginKey()];
user.CreateNewUser(newUserAttrs.login, newUserAttrs.password, newUserAttrs.email, function(res, err) {
if(!err) {
return usersByLogin[login] = newUserAttrs;
}
else {
throw err;
}
});
})
in another file I have write this code
exports.CreateNewUser = function(login, pass, mail, callback) {
var sql = "insert into `user`(login,mail,pass) values(?,?,?)";
client.query(sql, [login, mail, pass], function(err, results, fields) {
if(!err) {
callback(results);
console.log('test')
}
else {
callback(results, err);
}
});
};
This code are working fine. I have tested him. the only problem is they are working synchronosly (as normal). Can someone explain me what thing I have done in wrong way that make it async. I want to get it done in sync way.
The current code give me error (it's make a entry in database and produce error on browser)
Error: Step registerUser of `password` is promising: userOrErrors ; however, the step returns nothing. Fix the step by returning the expected values OR by returning a Promise that promises said values.
at Object.exec (E:\proj\Node\node_modules\everyauth\lib\step.js:68:11)
at E:\proj\Node\node_modules\everyauth\lib\stepSequence.js:26:38
at [object Object].callback (E:\proj\Node\node_modules\everyauth\lib\promise.js:13:12)
at RouteTriggeredSequence._bind (E:\proj\Node\node_modules\everyauth\lib\stepSequence.js:25:20)
at RouteTriggeredSequence.start (E:\proj\Node\node_modules\everyauth\lib\stepSequence.js:52:33)
at RouteTriggeredSequence.routeHandler (E:\proj\Node\node_modules\everyauth\lib\routeTriggeredSequence.js:13:13)
at Object.<anonymous> (native)
at nextMiddleware (E:\proj\Node\node_modules\connect\lib\middleware\router.js:175:25)
at param (E:\proj\Node\node_modules\connect\lib\middleware\router.js:183:16)
at pass (E:\proj\Node\node_modules\connect\lib\middleware\router.js:191:10)
Thanks
The two pieces of code you present are asynchronous and not synchronous!
With everyauth, to be able to handle asynchronous user creation you should use a Promise. So your code will be something like :
registerUser(function(newUserAttrs) {
var promise = this.Promise();
var login = newUserAttrs[this.loginKey()];
user.CreateNewUser(newUserAttrs.login, newUserAttrs.password, newUserAttrs.email, function(res, err) {
if(!err) {
return promise.fulfill(newUserAttrs);
}
else {
promise.fulfill(user);
}
});
})
Without promise you couldn't be sure that your new user has been added in your database. But if it doesn't matter you could have something like that:
registerUser(function(newUserAttrs) {
var login = newUserAttrs[this.loginKey()];
user.CreateNewUser(newUserAttrs.login, newUserAttrs.password, newUserAttrs.email, function(res, err) {
if (err) console.log(err);
});
return newUserAttrs;
})
Because you are doing a database query, this code has to be asynchronous. The anonymous function you pass to client.query will not be called until the database query is complete, so your callback gets called asynchronously.
You will need to treat this all as asynchronous, so for instance you'll have to trigger some other callback instead of returning the user object/throwing.
Related
I fixed this the right code is below
Im using mongodb and im trying to create a authentication function isAuthenticated that returns true or false if the user is in the database through a callback function. However I can't bring callback inside of client.connect . Im wondering why this is and what I could do to do this.
function isAuthenticated(username, password, callback1)
{
//callback1(true); <<<<<<<<<THIS WORKS HERE
client.connect(function(err, callback1) { //client.connect is the mongodb client object
//callback1(true); <<<<<<< THIS DOESNT WORK HERE SAYS callback is not a function
});
}
////
////
//this is how im calling the isAthenticated function in my route
isAuthenticated("user1", "pass1", function(result)
{
console.log("callback ran");
console.log("result: " + result);
});
right code:
function isAuthenticated(username, password, callback1)
{
return callback1(true); <<<<<<<<<THIS WORKS HERE
client.connect(function(err) { //client.connect is the mongodb client object
return callback1(true); <<<<<<< WORKS NOW
});
}
////
////
//this is how im calling the isAthenticated function in my route
isAuthenticated("user1", "pass1", function(result)
{
console.log("callback ran");
console.log("result: " + result);
});
Turn isAuthenticated into a promise and resolve instead of using a callback is quick and (in my limited experience) better for controlling the flow. That's if you can use promises of course.
function isAuthenticated(usename,pass){
return new Promise((resolve,reject)=>{
... insert code here...
})});
isAuthenticated("user1", "pass1")
.then((result)=>
{
console.log("callback ran");
console.log("result: " + result);
});
You have a very big misunderstanding of how functions work in javascript. First, let's fix your bug. Simply remove the second callback1 in your code:
function isAuthenticated(username, password, callback1)
{
client.connect(url, function(err, db) {
callback1(true); <<<<<<< THIS WORKS PERFECTLY FINE
});
}
Explaining the basics of how all this works in javascript is beyond the scope of a single answer. It involves first-class functions, anonymous functions and closures - that's at least two question's worth of answers. I suggest you google "javascript first class functions" and watch/read through the articles and tutorials.
I have an quite simple application the idea is that someone has unique code which value are stored in one mongo collection in other we are keeping some data which we need to return if the key was found in first collection.
As probably you have noticed I'm using NodeJS with MongoDB and Mongoose, Express.
I have a problem with method bellow:
exports.getCompanyByKey = function(req, res) {
console.log(req.params.keyvalue);
var query = Company.where({keyValue : req.params.keyvalue});
query.findOne(function(err, company){
if(err){
res.send(err);
}else{
SampleData.findOne({}, function(err, sample_data){
if(err)
res.send(err);
res.json(sample_data);
});
}
});
};
The problem is that it will always return the data beause it's not throwing an error but empty array - so is there any other good and proper way as it should be don to throw 404 error without statement such as if(length<0) res.status(404).send('Error message).
I simply want to minimalize amount of if statements.
Maybe there is some other way to write implementation od error handling for mongoose which in general instead returning empty array will give us error code with message?
It's not exactly clear what you're asking, but if you want to make an error condition out of something that is not normally an error, then an if statement (or some other test like that) is required to test for that specific condition.
You could make your own function for querying that turns an empty response into an error and you could "hide" the if condition in that function if you want, but it's still an if condition that tests for your specific condition.
So, to return a 404 if the array is empty, you would just add an if statement (as you already appear to know):
exports.getCompanyByKey = function(req, res) {
console.log(req.params.keyvalue);
var query = Company.where({keyValue : req.params.keyvalue});
query.findOne(function(err, company){
if(err){
res.status(500).send(err);
} else {
SampleData.findOne({}, function(err, sample_data){
if(err) {
res.status(500).send(err);
} else {
if (sample_data.length) {
res.json(sample_data);
} else {
res.status(404).send("no data");
}
}
});
}
});
};
FYI, you also need to make sure you are properly setting a status code when there's an error and that you are never sending multiple responses to the same request (even when there's an error). I've also fixed several cases of those issues in your code.
This could likely be written cleaner and responses consolidated by using the promise interface to your database and send an error in one .catch().
For example, you could simplify your code by creating a utility function for .findOne() that detects and sends an error response automatically:
function findOne(res, db, q, cb) {
db.findOne(q, function(err, data) {
if (err) {
res.status(500).send(err);
cb(err);
} else if (!q.length) {
res.status(404).send("no data");
cb(new Error("no data"));
} else {
cb(null, data);
}
});
}
Then, your function could be simplified to this:
exports.getCompanyByKey = function(req, res) {
var query = Company.where({keyValue : req.params.keyvalue});
query.findOne(function(err, company){
if(err){
res.status(500).send(err);
} else {
findOne(res, SampleData, {}, function(err, sample_data) {
// any error response has already been sent
if (!err) {
res.json(sample_data);
}
});
}
});
};
Again, this would be better to use your Db's promise interface.
Accordingly with nodejs sqlite library (https://github.com/mapbox/node-sqlite3) you can serialize a sql transaction with db.serialize function. All executions in this function will be executed in the specified order.
But with a for loop how can i get the information about the transaction will be success or fail?
function transaction(callback) {
db.serialize(function() {
db.run("BEGIN");
for(...) {
db.run("INSERT", function(err) {
//this code is not serialized with the parent serialize function (this is my problem)
//if one run throw error i must resolve the callback with the error code
if (err)
callback("error");
});
}
db.run("END");
callback("ok");
}
}
Before start transaction you can define some vars e.g. var results = [];. On complete each query inside callback you can collect error or null as results.push(err) and check results.length. When results.length equals to length of for then all queries executed and now check results.every((i) => i === null) value. If true then do commit else rollback.
Much better to control flow is use async module.
'use strict'
var async = require('async');
...
db.run('begin', function(err){
if (err)
return console.log(err);
let run = (sql, callback) => db.run(sql, callback);
async.eachSeries(sqls, run, function(err){
db.run((err) ? 'rollback' : 'commit', ...)
})
})
If you add begin to sqls then code will be more simple.
The 'GetUsers' function is running and it returns the correct values as expected. However, res.send(users) is running before the nested function has completed with the value so it is undefined. I have tried doing the res.send(users) from inside the nested function but it doesn't recognize the function. I have also tried using return users but can't get it to work. Any help would be appreciated!
app.get('/GetUsers', function (req, res){
var url = 'mongodb://localhost:27017/users';
var users = "";
MongoClient.connect(url, function (err, db) {
if (err) {
console.log('Unable to connect to the mongoDB server. Error:', err);
} else {
console.log('Connection established to', url);
// Get the documents collection
var collection = db.collection('users');
// Get some users
collection.find().toArray(function (err, res) {
if (err) {
console.log(err);
} else {
console.log('Got Information from db.');
}
//Close connection
db.close();
console.log('db closed.');
users = res;
console.log(users);
});
}
});
res.send(users);
});
Just move res.send(users) inside your callback before/after you call console.log(users).
You reused the variable res in the toArray call, so please rename it there: ...toArray(function (err, res)... to ...toArray(function (err, result)... and also in users = res; to users = result;.
this is async call it will send the response and will not wait for the database connection function.
write res.send in call back function
write your res.send function where you are closing your data base connection.
here is detail How do I return the response from an asynchronous call?
Found the answer!!!
I needed to use res.send(users) in my callback after I had closed the database as pointed out by the users suggestions above. HOWEVER, that alone wasn't working as res.send(users) was erroring as it wasn't recognized as a function. I had to use res.send({'matched':users}) and then access my response objects on the client side like this: response.matched.objects etc.
Thanks for your help everyone!
Ok, lets say I have two Models. Contract and CommLog. Both work find independently but I need many CommLog to relate to each Contract.
In the ContractSchema trying async
ContractSchema.methods.getCommLog = function getCommLog(){
var log = false;
async.parallel([
function(){
CommLog.find({commType:'contract',parent:this._id},function(err,comms){
log = comms;
});
}],
function(){return log;});
};
Where I am trying to use it
router.get('/:code', function(req, res, next) {
Contract.findOne({accessCode:req.params.code},function(err,contract){
if(err)
res.send(err);
var data;
if(contract != null){
var comms = contract.getCommLog();
data = {error:false,data:contract,commlog:comms}
}else{
data = {error:true,message:"No Contract"}
}
res.json(data);
});
});
Where it shows var comms = contract.getCommLog(); It is never returning anything because the getCommLog() is not executing async...
I think its my misunderstanding of mongoose querying, so if you understand what I am trying to accomplish, please let me know what I am doing wrong. I have tried without async which would always return false.
The find call can return all matching results with one query, so I don't think you need async here. The reason it is not populating correctly when you call res.json(data) is because you are not waiting for the method call to finish before you fire off your server response. You would be better off nesting an additional CommLogs.find call within the Contract.find call, and only sending your response once that finishes.
//pseudo code:
Contract.find({}, function(err, contract) {
if(err || !contract) {
//return error response
}
else {
CommLogs.find({contract: contract._id}, function(err, commlogs) {
if(err || !commlogs) {
//return error response 2
}
else {
res.json({errors: false, contract: contract, commlogs: commlogs});
}
});
}
}