NodeJS mysql query returns empty result - mysql

i'm new to NodeJS (duh!).
I know it executes functions asynchronous but I still cannot see what causes this phenomenon:
I am using the express and mysql modules and trying to execute an SQL query based on request parameters. It is supposed to be a simple validation API feature where the server is going to lookup a user in a database by listening on a specific URL for two request parameters (user and passwd).
The problem is that the SQL query always returns an empty object as result when I do this using the request parameters in the query.
However, if i hard code the query and run it outside the app.get(...) I get the desired result! But I need this to work on demand by request...
(I'm not intending to use GET-request later on, this example is for debugging purposes :))
What am i doing wrong here?
Here's my code:
// Server and Mysql setup here
var app = require('express').createServer(),
SERVER_PORT = 8080;
var Client = require('mysql').Client,
client = new Client(),
...
// User, password and database setup here, cropped out from this example //
// ...
function validateUser(user, passwd, callback) {
client.query('SELECT date FROM '+CUSTOMERS_TABLE+' WHERE email="'+user+'" AND passwd="'+passwd+'";',
function selectCb(err, results, fields) {
if (err) {
throw err;
}
console.log(fields);
callback(results);
});
}
app.get('/', function(req, res){
var url_parts = url.parse(req.url, true);
var query = url_parts.query;
if((typeof query[REQ_PARAM_USER] != 'undefined' && typeof query[REQ_PARAM_PASSWD] != 'undefined')
&& (query[REQ_PARAM_USER] != '' && query[REQ_PARAM_PASSWD] != '')) {
validateUser(REQ_PARAM_USER, REQ_PARAM_PASSWD, function(results) {
console.log(results);
});
}
res.end("End")
});
app.listen(SERVER_PORT);
console.log('Server running at port '+SERVER_PORT);
Oh, and by the way, console.log(fields) outputs the correct fields! But why not the results?

You are passing the wrong parameters to validateUser:
validateUser(REQ_PARAM_USER, REQ_PARAM_PASSWD, // ...
What you really want:
validateUser(query[REQ_PARAM_USER], query[REQ_PARAM_PASSWD], // ...
Edit: A few other issues with your code:
You don't have to parse the url. Express does this for you, and the query is available as req.query.
You shouldn't throw in asynchronous code. It will give unexpected results. Instead, stick to the nodejs paradigm of passing (err, results) to all callbacks, and do proper error checking where you can -- i.e., in your verifyUser, pass along the error with the callback and check for errors in your get handler. Either res.send(500) (or something) when you get an error, or pass it along to the express error handler by calling next(err).
validateUser(query[REQ_PARAM_USER], query[REQ_PARAM_PASSWD], function(err, results) {
if(err) {
console.error(err);
res.send(500);
} else {
console.log(results);
res.send(results);
}
});
Never pass query parameters directly to something like an SQL query. Instead, use parameters for your SQL query:
client.query('SELECT date FROM '+CUSTOMERS_TABLE+' WHERE email=? AND passwd=?', [user, passwd], // ...

Related

Node.js MySQL ERR_HTTP_HEADERS_SENT

This is the function in question:
castVote: function (req, res, ip, mysql) {
// POST return codes
const ALREADY_VOTED = '100';
const SUCCESS = '200';
const FAILURE = '300';
// Create connection to db
// Keep connection while the client is connected
var con = mysql.createConnection({
host: sqlHOST,
user: sqlUSER,
password: sqlPASS,
database: dbNAME
});
// Connect to db
con.connect(function (err) {
// MySQL error, return
if (err) {
res.send(FAILURE);
return;
}
});
// Get link voted for
var link = req.body.song_voted;
// Check if user has already voted
con.query(`SELECT * FROM VotedUsers WHERE ip = '${ip}'`, function (err, result, fields) {
if (err) {
res.send(FAILURE);
return;
}
// User hasn't voted
if (!result.length) {
// Cast vote here
// ...
// Add user's IP to voted list
con.query(`INSERT INTO VotedUsers (ip) VALUES ('${ip}')`, function (err, result) {
if (err) {
res.send(FAILURE);
return;
}
});
res.send(SUCCESS);
return;
}
// User already voted
else {
res.send(ALREADY_VOTED);
return;
}
});
}
I call the function like this, every time the user clicks a button
mysql_backend.castVote(req, res, uipv4, mysql);
(mysql is the mysql module. uipv4 is the user's ip).
Every time I click said button, This is the error output:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
I've got 2 questions:
What's wrong with my code?
Is there a better way of managing a mysql connection for each client (not opening a new one every time the user clicks the button)
1. What is wrong with your code
I can see a couple of things.
Firstly, the most obvious problem is your con.connect and con.query functions.
You have them implemented incorrectly. con.connect takes a callback that all your implementation should be in i.e. all your query logic, once you have created a connection.
Anyway the con.connect function is not required. You can remove it as con.query will create a connection for you.
Secondly, and this is causing the error that you are seeing. You need to check your application to ensure that the res.send (or equivalent) is only being invoked once per request.
So for example, this is going to throw the error you are seeing.
// User hasn't voted
if (!result.length) {
// Cast vote here
// ...
// Add user's IP to voted list
con.query(`INSERT INTO VotedUsers (ip) VALUES ('${ip}')`, function (err, result) {
if (err) {
res.send(FAILURE);
return;
}
});
res.send(SUCCESS);
return;
}
If the insert fails, res.send(SUCCESS) has already being executed, then the con.query callback is executed and the res.send(FAILURE) will be called.
This is just one example. You will need to check your code through the entire request to ensure that a response is only ever sent once. If you send more than once you will see the ERR_HTTP_HEADERS_SENT (or a similar error - depending on the function you invoke on the response object). The key takeaway is that you should only send a response once and only once! If the below is not fixing your error you need to check the rest of your application to make sure that you are not sending a response again e.g. after you call mysql_backend.castVote(req, res, uipv4, mysql);
To fix the above problem depends on the desired result.
If you want to return to the caller that the error has occurred, you must wait until the callback has returned. e.g.
if (!result.length) {
// Cast vote here
// ...
// Add user's IP to voted list
con.query(`INSERT INTO VotedUsers (ip) VALUES ('${ip}')`, function (err, result) {
if (err) {
res.send(FAILURE);
return;
}
res.send(SUCCESS);
return;
});
}
2. Is there a better way of Managing Connections
Yes - use connection pools. Connection pooling allows you to limit the number of connections that can be created, e.g. if you have 100 parallel requests and a maximum pool size of 10, you will only ever create 10 connections.

Testing an AWS Lambda function

I need to create a lambda function to act as the middleman between a mobile Java app and an AWS RDS MySQL database. The idea is to submit queries from the mobile app and then send them off to the lambda function, which will then return the query. I have a basic MySQL query set up in my AWS lambda:
var mysql = require('mysql');
var config = require('./config.json');
var pool = mysql.createPool({
host : config.dbhost,
user : config.dbuser,
password : config.dbpassword,
database : config.dbname
});
exports.handler = (event, context, callback) -> {
context.callbackWaitsForEmptyEventLoop = false;
pool.getConnection(function(err, connection) {
if (err) throw err; // not connected!
// Use the connection
connection.query('select Album from record', function (error, results, fields) {
// When done with the connection, release it.
connection.release();
// Handle error after the release.
if (error) callback(error);
else callback(null, results[0].Album);
// Don't use the connection here, it has been returned to the pool.
});
});
};
And all that I am currently trying to do is get this code to run and output what the query will return. I've seen tutorials where people seem to just click test and have the code run, but it keeps asking me to create a test, and I'm not sure what exactly I would need to do to test this function.
EDIT: I realized I was missing a small change in my lambda uploaded code, but I am now getting an error on line 10 saying there is an unexpected token >.
I'm not sure what's wrong here, as the tutorial I watched seems to have the same exact thing.
Since you're not passing in any parameters through the context, you can just create a test with the defaults or an empty object {}, and click Test in the console. It will invoke your Lambda function as if it had been called from your mobile app, and you can debug from there.

Return data from database.query() using sailsjs

I am trying to build an api using sailsjs that calls stored procedures of a MYSQL database. I decided to decouple the query by adding it to a service so that others functions might be able to call them and use them. Below is what I came up with.
under /api/controller/MySqlController
getAllUsers: function (req, res) {
MySqlSpService.spGetAllUsers(function(err, result){
if(err) return res.serverError(err);
return res.ok(result[1]);
});
},
under /api/services/MYSQLService
var MySqlSpService= {
spGetAllUsers: function(callback) {
Database.query('call userDb.sp_get_all_users(#message, #mystatus)', function (err, results) {
callback(err, results);
}); // end query
}
module.exports = MySqlSpService;
When I hit the api the data is displayed exactly how I thought it would be. But the problem is that when I try to call the spGetAllUsers service and assign to a variable, I get a undefined value.
Like this:
var users = MySqlSpService.spGetAllUsers(function(err, result){
if(err) return res.serverError(err);
return result[1];
});
I believe the problem is with the callbacks but I am not sure how to retrieve the data from the query. I have searched for an answer but I can't seem to find the right search terms that match my problem. Any help would be greatly appreciated Thanks in advance.
Indeed, your problem is about callback and asynchronous code.
The MySqlSpService.spGetAllUsers() function does not return anything, there is no return statement in this method. But it executes a callback function where you have the opportunity to execute code that depends on the SQL query result.
You have to write your code like this because the database query is executed asynchronously.
console.log('This is executed first');
MySqlSpService.spGetAllUsers(function(err, result){
console.log('This is executed when the result of the database query is received');
if(err) return res.serverError(err);
users = result[1];
/**
* Here you can access to the "users" data
* and execute code depending on it
*/
});
console.log('This is executed in second and it is not possible to know the result of the query yet');
Tools like async can help you to organize your asynchronous code. By default, async is available globally in sails.js.

Node Mysql Cannot Enqueue a query after calling quit

where do i close the mysql connection?
I need to run queries in sequence. I am writing code that looks like this at present:
var sqlFindMobile = "select * from user_mobiles where mobile=?";
var sqlNewUser = "insert into users (password) values (?)";
//var sqlUserId = "select last_insert_id() as user_id";
var sqlNewMobile = "insert into user_mobiles (user_id, mobile) values (?,?)";
connection.connect(function(err){});
var query = connection.query(sqlFindMobile, [req.body.mobile], function(err, results) {
if(err) throw err;
console.log("mobile query");
if(results.length==0) {
var query = connection.query(sqlNewUser, [req.body.password], function(err, results) {
if(err) throw err;
console.log("added user");
var user_id = results.insertId;
var query = connection.query(sqlNewMobile, [user_id, req.body.mobile], function(err, results) {
if(err) throw err;
console.log("added mobile");
//connection.end();
});
});
}
});
//connection.end();
(I am a beginner with node, npm-express and npm-mysql. I have tried searching SO for "express mysql cannot enqueue" to find related questions and have not found them.)
I fixed this problem use this method:
connection.end() in your connection.query function
The fixed code is here
If you're using the node-mysql module by felixge then you can call connection.end() at any point after you've made all of the connection.query() calls, since it will wait for all of the queries to finish before it terminates the connection.
See the example here for more information.
If you're wanting to run lots of queries in series, you should look into the async module, it's great for dealing with a series of asynchronous functions (i.e. those that have a callback).
Maybe the problem is that the mySQL query is executed after the connection is already closed, due to the asynchronous nature of Node. Try using this code to call connection.end() right before the thread exits:
function exitHandler(options, err) {
connection.end();
if (options.cleanup)
console.log('clean');
if (err)
console.log(err.stack);
if (options.exit)
process.exit();
}
//do something when app is closing
process.on('exit', exitHandler.bind(null, {cleanup: true}));
Code adapted from #Emil Condrea, doing a cleanup action just before node.js exits
In my case connection.end was being called in a spot that was hard to notice, so an errant call to connection.end could be the problem with this error

How to do a query to mysql database AFTER client`s request in nodejs/socket.io?

All examples i've seen were doing a query first and then send to client some info.
If I do a query FIRST and then use results in functions it works:
client.query(
('SELECT * FROM '+TABLES_USERS),
function(err, results, fields) {
var Users = (results);
io.sockets.on('connection', function (socket) {
socket.on('event1', function (data) {
var requser = Users[data];
socket.emit('event2', requser);
});
});
client.end();
});
But now i need to do a query on client's request.
I tried something like this but query doesn't work:
io.sockets.on('connection', function (socket) {
socket.on('event1', function (data) {
console.log('query required'); /*works*/
client.query(
('SELECT * FROM '+TABLES_USERS+' WHERE id_user ='+data),
function(err, results, fields) {
if (err) {throw err; /*doesn't work*/
console.log('error'); /*doesn't work*/ }
console.log('query is done'); /*doesn't work too. so i think query just doesn't work cuz there are no error and no results*/
socket.emit('event2', results);
client.end();
});
});
});
There are some things you are not doing ok (in my opinion) in the example above:
1) You don't ask for login after the client is connected to Socket.IO, instead you check to see if his session contains data that can verify is he's connected.
You should read the article about Express, Socket.IO and sessions., since it explains everything in detail (if you are using Express)
2) I think MongoDB, CouchDB and possibly other databases are better suited for realtime applications than MySQL.
I've solved the problem by using node-mysql-libmysqlclient instead of node-mysql. But if someone knows a way to do a query AFTER client's request in node-mysql, please tell me))