I am miss understanding this concept in nodejs. I want to place a function in a folder at ./models/user lets say to represent a model I use for user. Then I want to use these as functions somewhere else. The issue I always run into is when do something like user.something it doesn't handle like a function. I am misunderstanding how this works.
The model would look something like this:
//model/user.js
function User() {
this.foo = null;
}
User.prototype.hashPass = function (password, callback) {
//Code that hashes a password
callback(err, hash);
};
User.prototype.insertUser = function (email, password, callback) {
//Code that inserts a user and returns some 'done' callback
callback(err, done);
};
module.exports = User;
And somewhere else in my program lets say passport.js I want to do this:
//config/passport.js
var User = require('../models/user);
var user = new User();
async.waterfall([
//how does this look
//EDIT ATTEMPTED HERE
user.hashPass(password, function(err, result) {
}),
user.insertUser(result, function(err, rows) {
})
], //some callback );
Made some edits to help to clarify what I am trying to accomplish here.
EDIT:
This link shows how to do async waterfalls with multiple callbacks
Code based on EDIT / Understanding:
async.series([
function(callback) {
user.hashPass(password, function(err, result) {
callback(err,result);
})
}
], function(err, result) {
if (err) return err;
console.log('test',result);
});
It's not working because you have to 'require' your module in the file you want to use it in and you're creating object methods on a constructor function that does not exist. Instead, you could create a user object (not a constructor function) and set each function to an object property, like this:
//module file
var user = {
hashPass: function(password, callback){
//password-hashing function
},
insertUser: function(email, password, callback){
//new user function
}
};
module.exports = user;
Then, in whatever place you want to use it, you do so like this:
//some other file
var user = require(path-to-module);
user.hashPass(); //pass in all parameters (password, callback)
user.insertUser(); //pass in all parameters (password, callback)
The only potential hang-up about this method is that you'll have to define all of your parameters before calling either object property.
The only thing you need to change in your code is to replace the line
module.exports = User;
in your passport.js file by
var User = require('../model/User');
Then you can invoke the functions on user:
user.hashPass(password, function(err, result) {
user.insertUser(result, function(err, rows) {
// do something with rows here
});
});
When we have been requiring custom modules we require them by path .
var User = require('config/passport');
var user = new User();
async.waterfall([
//how does this look
//do hashPass here
,
//do insertPass here
], //some callback );
Related
I am working with Express and MySQL and I would like to be able to retrieve data from a connection.query and then use that data in other functions within the same route. I would like to be able to use the array made inside of the connection.query inside of another function in app.post, however I don't know what the best way to go about doing this is. I have tried to use utility functions written in a separate file required by Express but I haven't been able to get data from the MySQL connection into the function written in the external file and I'm not even sure if that's the proper way to do what I want.
Here is my code, what I'd like to do is possibly be able to put the forEach method inside of its own function and then use the returned array in another function after the query to do something with it and so on.
app.post('/submit', function(req, res) {
var getPosition = "SELECT ID, position FROM routes";
var posArray = [];
connection.query(getPosition, function(err, results) {
if (err) throw err;
results.forEach(function(item) {
posArray.push(item.position);
return posArray;
}); //end if
});
}); // end app.post
You can chain middleware to achieve this:
app.post('/submit', getDataFromDatabase, processData); // end app.post
function getDataFromDatabase(req, res, next) {
var getPosition = "SELECT ID, position FROM routes";
var posArray = [];
connection.query(getPosition, function(err, results) {
if (err) throw err;
results.forEach(function(item) {
posArray.push(item.position);
req.posArray = posArray; //attach the data that you want to use later to your req object
}); //end if
next(); //Call the next middleware;
});
}
function processData(req, res, next){
//Do whatever you want with req.posArray;
res.json(posArray); //respond to the client with the posArray (or whatever the client asked for)
}
This question might sound duplicate of the one here but my scenario is quiet different. I have getSystemIds.js file like this:
var system_ids_list = {};
var getSystemIDs = function(req, callback) {
var client = //creating an object using internally developed node library
client.request('sql_parameter', {'Query.ID' : query_number},
function(err, req, res){
//Do some stuff to calculate necessary values
system_ids_list[key_value] = value_array;
i+= 1;
}
return(req, callback)
}
)
};
module.exports = getSystemIDs;
Now, as shown in the answer in the link above, I am doing this in app.js
appSSL.get('/sysids', function(req, res) {
var sys_ids = system_ids_list(req, function(err, sys_ids) {
res.render('sysids', sys_ids);
})
});
I am not getting any specific error but the web page never loads as if something is stuck in the process or it does not know where to go next. Can someone help me figure out what would be the best way to do this?
Your getSystemIds() function is never calling the callback that was passed to it so the caller of getSystemIds() never gets a result - thus nothing ever happens on the request.
Change it to this:
var system_ids_list = {};
var getSystemIDs = function (req, callback) {
var client = //creating an object using internally developed node library
client.request('sql_parameter', {'Query.ID': query_number}, function (err, req, res) {
//Do some stuff to calculate necessary values
system_ids_list[key_value] = value_array;
i += 1;
// call the callback now to communicate back the async results
callback(null, system_ids_list);
});
};
module.exports = getSystemIDs;
The way you have your code structured, system_ids_list will accumulate more and more values each time getSystemIDs() is called. That seems a bit of an odd way to structure things so I'm pointing that out in case that is not really what you intend.
Also, your getSystemIDs() function does not return anything so you should change this:
appSSL.get('/sysids', function(req, res) {
var sys_ids = system_ids_list(req, function(err, sys_ids) {
res.render('sysids', sys_ids);
});
});
to this to make it less missleading about what is going on:
appSSL.get('/sysids', function(req, res) {
system_ids_list(req, function(err, sys_ids) {
res.render('sysids', sys_ids);
});
});
And, if res.render() is from a system like ExpressJS, then you probably want to be passing an object and naming a template:
res.render('sometemplate.html', {sysids: sys_ids});
If you want system_ids_list to not accumulate values, but to return a fresh value each time, you can define it within your function like this:
var getSystemIDs = function (req, callback) {
var system_ids_list = {};
var client = //creating an object using internally developed node library
client.request('sql_parameter', {'Query.ID': query_number}, function (err, req, res) {
//Do some stuff to calculate necessary values
system_ids_list[key_value] = value_array;
i += 1;
// call the callback now to communicate back the async results
callback(null, system_ids_list);
});
};
module.exports = getSystemIDs;
I got a file newuser.js (node.js environment featuring a mongodb database managed via mongoose) containing the following code:
//newuser.js
//basically creates new user documents in the database and takes a GET parameter and an externally generated random code (see randomcode.js)
[...]
var randomCode = require ('randomcode');
var newTempUser = new tempUser({name: req.body.name, vericode: randomCode.randomveriCode(parameter)
});
newTempUser.save(function (err){
//some output
});
//randomcode.js
//creates a random sequence of characters (=vericode), checks if code already exists in DB and restarts function if so or returns generated code
exports.randomveriCode = function randomveriCode(parameter){
[...]
var TempUser = conn.model('TempUser', TempUserSchema);
TempUser.count({vericode: generatedcode}, function(err, counter){
if (counter=='0'){
return generatedcode;
}else{
randomveriCode(parameter);
}
});
};
Problem is, that newuser.js throws an error as variable vericode is 'undefined' (thus mongoose model validations fails). The error does not occur if I skip the database query and instantly return the generated code (which in fact has got a value as verified by several console.log instructions). It occurs to me that the db query takes to long and empty or null value returned before query is complete? I thought about introducing promises unless you got any other suggestions or hints what may cause this behaviour?
Kind regards
Igor
Since querying the database is a non-blocking operation, you cannot expect the function call to return the value from the database immediately. Try passing in a callback instead:
// newuser.js
var randomCode = require('randomcode');
randomCode.randomveriCode(parameter, function(err, code) {
if (err) throw err; // TODO: handle better
var newTempUser = new tempUser({name: req.body.name, vericode: code});
newTempUser.save(function (err){
//some output
});
});
// randomcode.js
exports.randomveriCode = function randomveriCode(parameter, cb) {
var TempUser = conn.model('TempUser', TempUserSchema);
TempUser.count({vericode: generatedcode}, function(err, counter) {
if (err) return cb(err);
if (counter == '0') {
cb(null, generatedcode);
} else {
randomveriCode(parameter, cb);
}
});
};
your randomveriCode function contains calls to an asynchronous function and therefore, your function really needs to provide a callback argument like this:
exports.randomveriCode = function randomveriCode(parameter, callback){
[...]
var TempUser = conn.model('TempUser', TempUserSchema);
TempUser.count({vericode: generatedcode}, function(err, counter){
if(err) return callback(err);
if (counter=='0'){
return callback(null, generatedcode);
}else{
randomveriCode(parameter, callback);
}
});
};
You'd then call it like so:
var randomCode = require ('randomcode');
randomCode(function(err, vericode){
if(err) throw err;
var newTempUser = new tempUser({name: req.body.name, vericode: vericode});
newTempUser.save(function(err,newUser){
//do something here
});
});
Btw - you could also use a synchronous function to create a GUID. See https://www.npmjs.org/package/node-uuid.
I would like to 'functionalize' my queries by putting them into functions which have apt names for the task.
I want to avoid putting everything in the req, res functions (my controllers), and instead put them in 'models' of sorts, that is, another JavaScript file that will be imported and used to run the functions that execute queries and return the results on behalf of the controller.
Assuming that I have the following setup for the queries:
UserController.js
exports.userAccount = function(req, res, next) {
var queryText = "\
SELECT *\
FROM users\
WHERE id = $1\
";
var queryValues = [168];
pg.connect(secrets.DATABASE_URL, function(err, client, done) {
client.query(queryText, queryValues, function(err, result) {
res.render('pathToSome/page', {
queryResult: result.rows
});
});
});
}
Here, while I'm in the query, I essentially redirect and render a page with the data. That works fine. But I want to take out all that pg.connect and client.query code and move it to a separate file to be imported as a model. I've come up with the following:
UserModel.js
exports.findUser = function(id) {
// The user to be returned from the query
// Local scope to 'findUser' function?
var user = {};
var queryText = "\
SELECT *\
FROM users\
WHERE id = $1\
";
var queryValues = [id];
pg.connect(secrets.DATABASE_URL, function(err, client, done) {
client.query(queryText, queryValues, function(err, result) {
// There is only ever 1 row returned, so get the first one in the array
// Apparently this is local scope to 'client.query'?
// I want this to overwrite the user variable declared at the top of the function
user = result.rows;
// Console output correct; I have my one user
console.log("User data: " + JSON.stringify(user));
});
});
// I expect this to be correct. User is empty, because it was not really
// assigned in the user = result.rows call above.
console.log("User outside of 'pg.connect': " + JSON.stringify(user));
// I would like to return the user here, but it's empty!
return user;
};
and I'm calling my model function as so:
var user = UserModel.findUser(req.user.id);
The query executes perfectly fine in this fashion - except that the user object is not being assigned correctly (I'm assuming a scope issue), and I can't figure it out.
The goal is to be able to call a function (like the one above) from the controller, have the model execute the query and return the result to the controller.
Am I missing something blatantly obvious here?
pgconnect is an asynchronous call. Instead of waiting for data to return from the database before proceeding with the next line, it goes ahead with the rest of the program before Postgres answers. So in the code above, findUser returns a variable that has not yet been populated.
In order to make it work correctly, you have to add a callback to the findUser function. (I told you wrong in a previous edit: The done parameter in pg.connect is called in order to release the connection back to the connection pool.) The final result should look something like this:
exports.findUser = function(id, callback) {
var user = {};
var queryText = "SELECT FROM users WHERE id = $1";
var queryValues = [id];
pg.connect(secrets.DATABASE_URL, function(err, client, done) {
client.query(queryText, queryValues, function(err, result) {
user = result.rows;
done(); // Releases the connection back to the connection pool
callback(err, user);
});
});
return user;
};
And you'd use it, not like this:
var user = myModule.findUser(id);
But like this:
myModule.findUser(id, function(err, user){
// do something with the user.
});
If you have several steps to perform, each of them dependent on data from a previous asynchronous call, you'll wind up with confusing, Inception-style nested callbacks. Several asynchronous libraries exist to help you with making such code more readable, but the most popular is npm's async module.
I am just starting out with mongodb, but I am running into a problem when trying to use .find() on a collection.
I've created a DataAccessObject which opens a specific databate and then lets your perform operations on it. Here is the code:
The constructor:
var DataAccessObject = function(db_name, host, port){
this.db = new Db(db_name, new Server(host, port, {auto_reconnect: true}, {}));
this.db.open(function(){});
}
A getCollection function:
DataAccessObject.prototype.getCollection = function(collection_name, callback) {
this.db.collection(collection_name, function(error, collection) {
if(error) callback(error);
else callback(null, collection);
});
};
A save function:
DataAccessObject.prototype.save = function(collection_name, data, callback){
this.getCollection(collection_name, function(error, collection){
if(error) callback(error);
else{
//in case it's just one article and not an array of articles
if(typeof (data.length) === 'undefined'){
data = [data];
}
//insert to collection
collection.insert(data, function(){
callback(null, data);
});
}
});
}
And what seems to be the problematic one - a findAll function:
DataAccessObject.prototype.findAll = function(collection_name, callback) {
this.getCollection(collection_name, function(error, collection) {
if(error) callback(error)
else {
collection.find().toArray(function(error, results){
if(error) callback(error);
else callback(null, results);
});
}
});
};
Whenever I try to dao.findAll(error, callback), the callback never gets called.
I've narrowed the problem down to the following part of the code:
collection.find().toArray(function(error, result){
//... whatever is in here never gets executed
});
I've looked at how other people do it. In fact, I'm following this tutorial very closely. No one else seems to have this problem with colelction.find().toArray(), and it doesn't come up in my searches.
Thanks,
Xaan.
You are not using the open callback so if you are trying to make the findall request right after creating the dao then it won't be ready.
If your code is like this, it will not work.
var dao = new DataAccessObject("my_dbase", "localhost", 27017);
dao.findAll("my_collection",function() {console.log(arguments);});
I tested it and it doesn't find records, and it also gives no error. I think it should give an error.
But if you change it so that you give a callback to the constructor, then it should work.
var DataAccessObject = function(db_name, host, port, callback){
this.db = new Db(db_name, new Server(host, port, {auto_reconnect: true}, {}));
this.db.open(callback);
}
And make your code like this.
var dao = new DataAccessObject("my_dbase", "localhost", 27017, function() {
dao.findAll("my_collection",function() {console.log(arguments);});
});