i am pretty new to java script and nodejs.
i am trying to write a code that checks if an email is already exists in the database, and if not send an error.
the problem is that before the database function is ending, my code going to the next line, resulting in undefined variable(emailExists)
this is my code for the sign Up:
app.post('/signUpWeb', function (req, res) {
var reqBody = req.body;
var email= reqBody.email;
var password= reqBody.password;
var fullName= reqBody.fullName;
var webDbInsertion = {email: email, password: password, fullName: fullName};
var emailExists= DButils.checkIfPKexists(connection, "webusersMail", "email", webDbInsertion.email);
if(emailExists == false){
DButils.insertInfoToDB(connection, "webusersMail" ,webDbInsertion);
console.log("successfull signup");
res.send("successfull signup");
}else{
console.log("signup failed, email: " + email + " allready exits");
res.send("signup failed");
}
res.end();
});
and this is for my database call
exports.checkIfPKexists= function(dbConnection, tableName, PK, newPK){
var query = dbConnection.query('select count(*) as mailPkCount from ' +tableName+ ' where ' +PK+ ' = ?', newPK, function (err, row, result) {
if (err) {
console.error(err);
return;
}
var count= row[0].mailPkCount;
var bool = (count > 0);
return bool;
});
};
Well you see, node.JS is designed to be async, while what you're asking isn't impossible, it would shut down the main thread that runs the event loop for that instance of your server.
What I suggest doing is adding a callback function to your "checkIfPKexists" function, and return with the result as a parameter, much like you do in the DBConnection.query. You would then move all the code that isn't getting initialized into your callback.
Edit: Heres a quick code example, polish it up to your liking http://pastebin.com/bEHp4bi2
Related
I am unable to execute the sql, when using the global database connection in node.js.
I have followed the steps as in Azure documentation: https://learn.microsoft.com/en-us/azure/mysql/connect-nodejs and able to display the output on the console. But, I want to put all my Azure SQL database connection in a separate file, but the select query is not printing the output on the console.
DatabaseManager.js
var Connection = require('tedious').Connection;
var Request = require('tedious').Request;
var sqlConnection = function sqlConnection() {
// Create connection to database
var config =
{
userName: 'uname',
password: 'password',
server: 'dbserver.database.windows.net',
options:
{
database: 'mydatabase',
encrypt: true
}
}
var connection = new Connection(config);
// Attempt to connect and execute queries if connection goes through
connection.on('connect', function(err) {
if (err)
{
console.log(err)
}
else
{
console.log('CONNECTED TO DATABASE');
}
}
);
}
module.exports = sqlConnection;
app.js
var restify = require('restify');
var builder = require('botbuilder');
var botbuilder_azure = require("botbuilder-azure");
var azure = require('azure-storage');
var dbconnection = require('./DatabaseManager');
bot.dialog('profileDialog',
(session) => {
session.send('You reached the profile intent. You said \'%s\'.', session.message.text);
console.log('Reading rows from the Table...');
dbconnection("select FNAME from StudentProfile where ID=1"),
function (err, result, fields) {
if (err) throw err;
console.log(result);
}
session.endDialog();
}
Console Output:
Reading rows from the Table...
CONNECTED TO DATABASE
I was expecting the output of FNAME, but nothing is printing on the console. Is there anything, I am missing?
Thank you.
There's a couple of problems here. First off, you should only ever import a module once per file. This is just a performance consideration and won't actually break your code.
Next, pay attention to what you're exporting from your DatabaseManager module. Right now, you're exporting a function that creates the connection and then doesn't do anything with it. We can fix this by using a pattern called a "callback" which lets us provide a function that will then be called with the connection as an argument.
I added a ton of comments to the code explaining things. This code won't run as-is - there's a couple places where I have "do this or this". You'll have to choose one.
var Tedious = require('tedious'); // Only require a library once per file
var Connection = Tedious.Connection;
var Request = Tedious.Request;
// Or using the object spread operator
var { Connection, Request } = require('tedious');
// You called this `sqlConnection`. I'm going to use a verb since it's a
// function and not a variable containing the connection. I'm also going
// to change the declaration syntax to be clearer.
function connect(cb) { // cb is short for callback. It should be a function.
var config = {
userName: 'uname',
password: 'password',
server: 'dbserver.database.windows.net',
options: {
database: 'mydatabase',
encrypt: true
}
}; // Put a semi-colon on your variable assignments
var connection = new Connection(config);
// Attempt to connect and execute queries if connection goes through
connection.on('connect', function(err) {
if (err) {
console.log(err);
return; // Stop executing the function if it failed
}
// We don't need an "else" because of the return statement above
console.log('CONNECTED TO DATABASE');
// We have a connection, now let's do something with it. Call the
// callback and pass it the connection.
cb(connection);
});
}
module.exports = connect; // This exports a function that creates the connection
Then back in your main file, you can use it like so.
var restify = require('restify');
var builder = require('botbuilder');
var botbuilder_azure = require('botbuilder-azure');
var azure = require('azure-storage');
var connect = require('./DatabaseManager'); // renamed to be a verb since it's a function.
bot.dialog('profileDialog', (session) => { // Hey, this is a callback too!
session.send('You reached the profile intent. You said \'%s\'.', session.message.text);
console.log('Creating a connection');
connect((connection) => {
// or with the traditional function notation
connect(function(connection) {
console.log('Reading rows from the Table...');
// Execute your queries here using your connection. This code is
// taken from
// https://github.com/tediousjs/tedious/blob/master/examples/minimal.js
request = new Request("select FNAME from StudentProfile where ID=1", function(err, rowCount) { // Look another callback!
if (err) {
console.log(err);
} else {
console.log(rowCount + ' rows');
}
connection.close();
});
request.on('row', function(columns) { // Iterate through the rows using a callback
columns.forEach(function(column) {
if (column.value === null) {
console.log('NULL');
} else {
console.log(column.value);
}
});
});
connection.execSql(request);
});
In my chat program, I am trying to create a function, that checks if there exists a conversation in the database. If a conversation exists with peopleName it should get retrieved on the client. If a conversation with that name does NOT exist, a new conversation should get created.
It seems that the 'checkConversation' function is not waiting for the result, because it is creating a new conversation every time, even though the conversation exists.
Client-side:
//Starting conversation
$("#people").on("click", ".list-group-item", function() {
var peopleName = $(this).children("span").text();
var peopleID = $(this).children("span").attr("class");
var conversationExists = false;
socket.emit("checkConversation", peopleName, function(data) {
conversationExists = data.result;
if (conversationExists) {
console.log("Retrieved existing conversation with ", peopleName);
return;
// Check if there is a conversation in the Database where this name is conversationReceiver. ------------------------------------
// if there is: retrieve conversation/messages
// else: create conversation.
} else {
console.log("NEW conversation with ", peopleName);
socket.emit("serverCreateConversation", peopleName, peopleID);
$("#msg").prop("readonly", false);
$("#msg").attr("placeholder", "Your message");
$("#send").attr("disabled", false);
$("#chat").empty();
}
});
});
Server-side:
client.on("checkConversation", function(peopleName, fn) {
var match = false;
connection.query("SELECT * FROM `conversations` WHERE `conversation_receiver` = '" + peopleName + "'", function(error, results, fields) {
if (error) {
console.log(error);
} else if (results) {
console.log("Conversation exists!", results);
match = true;
} else {
console.log(fields);
}
});
console.log("match: " + match);
fn({ result: match });
});
This seems like the usual asynchronous callback hurdle people stumble upon when starting with Node.js and other Javascript asynchrony.
Server-side, you need to only call fn when you get the result from the database, i.e. in the callback you pass to connection.query:
client.on("checkConversation", function(peopleName, fn) {
connection.query("SELECT * FROM `conversations` WHERE `conversation_receiver` = '" + peopleName + "'", function(error, results, fields) {
if (error) {
console.error(error);
fn({ result: false, error: true });
return;
}
var match = !!results; // (shorthand to turn a truthy value into true/false)
console.log("Conversation with " + peopleName + ": " + match);
fn({ result: match });
});
});
(I took the liberty of simplifying the code a little, too.)
However, there's another pressing issue: Your code is vulnerable to SQL injection attacks. Please find out how parametrized queries work in the SQL library you're using, and use them instead of building SQL queries with +!
You're doing it wrong, websocket don't work like AJAX, you need to emit the result from your backend, and listen to it on your frontend
you need a socket.emit on your server code
and you need a socket.on on your client code
Good day again, hoomans!
I am working with this small web app with NodeJS. I have a couple of search functionalities across my web pages. But I want to ask something about nested function in regard to this search functionality.
There is actually no error in my code. It's working pretty well for now, but the reason I am asking this is I don't quite trust my algorithm.
Originally, the structure is that on the back-end part of the search query, the entire code block including the search query to the database is written inside the app.post() method inside my routes/grades.js file. The front end on the other hand is written in AngularJS in separate file located in public/javascripts directory, which should dynamically reload the table elements upon the request using a submit button.
I think this is not a good algorithmic concept because I am thinking of a scenario where multiple users would access the app.post() method and might cause some kind of problem I don't know.
So now, I am thinking if I can make use of nested function with callback and timeout. I cannot describe this anymore clearly so I'll just paste the code in.
Original code in my route:
routes/grades.js
app.post('/grades', function(req, res, next){
setTimeout(callback, 100);
try{
var reqObj = req.body;
console.log("Request Object: " + reqObj["className"]);
req.getConnection(function(err, conn){
if(err){
console.error('SQL Connection error: ', err);
return next(err);
}
else{
var insertSql = "SELECT Class_Name,Class_Code,Class_Instructor FROM classes WHERE Class_Name LIKE ?";
var insertValues = [
'%' + reqObj["className"] + '%'
];
var query = conn.query(insertSql, insertValues, function(err, result){
if(err){
console.error('SQL error: ', err);
return next(err);
}
var class_array = [];
for(var i=0; i<result.length; i++){
class_array.push(result[i]);
}
console.log(class_array.valueOf());
res.send([{
info:
class_array.valueOf()
}])
});
}
});
}
catch(ex){
console.err("Internal error: " + ex);
return next(ex);
}
});
Now, I revised the code in routes/grades.js above and made it something that makes more sense (I think lol), and it works without any error. But then, I still want to know which of these codes have better algorithm, or if they both suck. Lol
routes/grades.js
router.post('/grades', function(req, res, next){
searchForClass(req, res, next, function(){
console.log("Class Search Success");
});
});
function searchForClass(req, res, next, callback){
setTimeout(callback, 100);
try{
var reqObj = req.body;
console.log("Request Object: " + reqObj["className"]);
req.getConnection(function(err, conn){
if(err){
console.error('SQL Connection error: ', err);
return next(err);
}
else{
var insertSql = "SELECT Class_Name,Class_Code,Class_Instructor FROM classes WHERE Class_Name LIKE ?";
var insertValues = [
'%' + reqObj["className"] + '%'
];
var query = conn.query(insertSql, insertValues, function(err, result){
if(err){
console.error('SQL error: ', err);
return next(err);
}
var class_array = [];
for(var i=0; i<result.length; i++){
class_array.push(result[i]);
}
console.log(class_array.valueOf());
res.send([{
info:
class_array.valueOf()
}])
});
}
});
}
catch(ex){
console.err("Internal error: " + ex);
return next(ex);
}
};
Meanwhile, this is my front-end code for the search.
public/javascripts/app.js
$scope.classFun = function(){
console.log('clicked submit');
$http({
url: 'http://localhost:3000/grades',
method: 'POST',
data: $scope.data
}).then(function (httpResponse){
console.log('response', httpResponse);
// console.log(Object.keys(httpResponse.data[0].info[0]).length);
var tbody = document.getElementById("class_list_data");
while(tbody.firstElementChild){
tbody.removeChild(tbody.firstChild);
}
for(var i=0; i<httpResponse.data.length; i++){
for(var j=0; j<httpResponse.data[i].info.length; j++){
var tr = document.createElement("tr");
var td = document.createElement("td");
td.appendChild(document.createTextNode(httpResponse.data[i].info[j].Class_Name.toString()));
tr.appendChild(td);
tbody.appendChild(tr);
}
}
})
}
I hope I expressed my problem correctly and I really hope you guys can help me.
Much thanks!
There is only one problem in your code, I don't think you need the callback function because it doesn't give any useful meaning.
setTimeout(callback, 100); //it fires the callback after 100ms no matter what
You don't have to worry about multiple client requests because each request will be running in different scope.
For performance wise, using MySQL string search with "LIKE" keyword is extremely slow when the table is going big and not indexed correctly.
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.
Problem with Promised Connections
I recently converted my Node app from running on my local machine to utilizing an Amazon EC2 for the Node app and a VPN for the file-serving and MySQL.
I learned just enough about Promises to write the following connection snippet (which runs 3 queries before responding to the client), utilizing Bluebird. The connections worked on my machine, but with the VPN hosted MySQL settings, the connections crashed every time, about 30 seconds after the app started, which I realized was probably because I'd forgotten to close them.
EDIT: Based on the comments, it appears the issue is not in the connection closures.
So I modified my script in the best way I knew to close the connections, but with Promises, this is confusing. This version of the connection doesn't work. It doesn't fail or cause any errors. It just returns no results on the server side. I think my problem is in the way I've closed the connections.
What's causing the issue?
Is it the connection closures?
If so, how would I close them properly?
My (Simplified) MySQL Connection Attempt with Bluebird Promises
var mysql = require('mysql');
var Promise = require('bluebird');
var moment = require('moment');
function createConnection() {
var connection = mysql.createConnection({
dateStrings : true,
host : 'hostname',
user : 'username',
password : 'password',
database : 'database'
});
connection = Promise.promisifyAll(connection);
return connection;
}
function sendGame(req, res, sales, settings, categories, players) {
var game = new Object();
game.sales = sales;
game.players = players;
game.settings = settings;
game.categories = categories;
var JSONgame = JSON.stringify(game);
console.log("Game: " + JSON.stringify(game, undefined, 4));
}
var retrieveSales = Promise.method(function (username, connection, timeFrame) {
console.log('User ' + username + ' retrieving sales...');
var q = 'select * from sales_entries where date BETWEEN ? AND ?';
return connection.queryAsync(q, timeFrame).then(function (results) {
return results[0];
});
});
var retrieveSettings = Promise.method(function (username, connection) {
console.log('User ' + username + ' retrieving settings...');
var q = 'select * from sales_settings';
return connection.queryAsync(q).then(function (results) {
return results[0];
});
});
var retrieveCategories = Promise.method(function (username, connection) {
console.log('User ' + username + ' retrieving categories...');
var q = 'select * from sales_categories';
return connection.queryAsync(q).then(function (results) {
return results[0];
});
});
var retrievePlayers = Promise.method(function (username, connection) {
console.log('User ' + username + ' retrieving players...');
var q = 'select * from users';
return connection.queryAsync(q).then(function (results) {
return results[0];
});
});
var gameSucceed = Promise.method(function gameSucceed(req, res) {
var username = req.body.username;
console.log('User ' + req.body.username + ' retrieving game...');
var timeFrame = [moment().days(0).hour(0).minute(0).second(0).format("YYYY-MM-DD HH:mm:ss"), moment().days(6).hour(0).minute(0).second(0).format("YYYY-MM-DD HH:mm:ss")];
//var connection = Promise.promisifyAll(createConnection());
return connection.connectAsync().then(function () {
console.log('Connection with the MySQL database openned for Game retrieval...');
return Promise.all([retrieveSales(username, connection, timeFrame), retrieveSettings(username, connection), retrieveCategories(username, connection), retrievePlayers(username, connection)]);
}).then(function () {
connection.end(),
console.log("...Connection with the MySQL database for Game retrieval ended")
});
});
function getGameData(req, res) {
gameSucceed(req, res).spread(function (sales, settings, categories, players) {
return sendGame(req, res, sales, settings, categories, players);
});
};
var req = new Object();
var res = new Object();
req.body = {
"username" : "user123",
"password" : "password"
}
getGameData(req, res);
Console Result
User user123 retrieving game...
Connection with the MySQL database openned for Game retrieval...
User user123 retrieving sales...
User user123 retrieving settings...
User user123 retrieving categories...
User user123 retrieving players...
...Connection with the MySQL database for Game retrieval ended
Game: {}
var gameSucceed = function gameSucceed(req, res) {
…
var connection = createConnection());
return connection.connectAsync().then(function () {
return Promise.all([…]);
}).then(function () {
connection.end();
});
};
The promise that is ultimately returned from this method does not have a resolution value. It is created by that then call from whose callback you do not return - which will lead to undefined. To fix this, just route the result through:
.then(function(results) {
connection.end();
return results;
});
However, if you do it like that the connection won't be closed in case of an error. The best solution is to use the finally() method, which just works like a finally clause in synchronous code. It's callback will be invoked both for resolutions and rejections, and the resulting promise will automatically carry on the value.
.finally(function() {
connection.end();
})
// .then(function(results) { })
Your code has a particular resource management problem like Bergi put it. You have to keep remembering when to close the collection and when not to.
The optimal solution would be to use Promise.using however, that's only available in the v2 branch of Bluebird so you're going to have to wait a while.
Until then, you can create your own wrapper method that does more basic scoped resource management:
function connect(fn,timeout){
timeout = (timeout === undefined) ? 8000 : timeout; // connection timeout
return createConnection().then(function(connection){
// run the function, when it resolves - close the connection
// set a 7 second timeout on the connection
return fn(connection).timeout(timeout).finally(function(){
connection.end();
});
});
}
Which would let you do:
connect(function(connection){
return gameSucceed(req,resp,connection); // connection is injected to that fn now
}).then(function(val){
// gameSucceed resolution value here
});
Now, when the gameSucceed is done, the connection will close itself automatically. This would make gameSucceed itself look like:
var gameSucceed = Promise.method(function gameSucceed(req, res,connection) {
var username = req.body.username;
console.log('User ' + req.body.username + ' retrieving game...');
var timeFrame = [moment().days(0).hour(0).minute(0).second(0).format("YYYY-MM-DD HH:mm:ss"), moment().days(6).hour(0).minute(0).second(0).format("YYYY-MM-DD HH:mm:ss")];
return connection.connectAsync().then(function () {
console.log('Connection with the MySQL database openned for Game retrieval...');
return Promise.all([retrieveSales(username, connection, timeFrame), retrieveSettings(username, connection), retrieveCategories(username, connection), retrievePlayers(username, connection)]);
}); // no longer its responsibility to handle the connection
});
Generally, you might also want to consider a more OOPish style of coding for your code.
Good luck, and happy coding.