The problem has been solved, but I'd like to leave this question with a deeper impression. Thanks for everyone!
update
I found something wrong in /src/mysql/index.js, this code
/* this is wrong */
MySqlConnection.connect((error) => {
/* old code */
throw new Error(error.message);
/* new code */
error && new Error(error.message);
});
After modification, i'm connected to MySQL.But i still can't get correct return , i got nudefined...
I tried using node to connect MySQL and operate it, but i failed and get no error message.
I used koa2 and mysql from npm packages.
"koa2": "^2.0.0-alpha.7",
"mysql": "^2.17.1",
My MySQL version is 8.0
I encapsulates a function to connect mysql, i'm sure that MySQL's configuration is useless.
/* /src/mysql/index.js */
const MySqlConnection = require("./mysql.index");
/**
* connnect mysql
* #param {string} sqlStatement sql statement
* #param {object} params some required parameters
* #param {function} callback if success
*/
const MySqlOperator = (sqlStatement, params, callback) => {
MySqlConnection.connect((error) => {
throw new Error(error.message);
});
MySqlConnection.query(sqlStatement, params, (error, result, fieldValues) => {
try {
callback && callback(result, fieldValues);
} catch {
throw new Error(error.message);
}
});
MySqlConnection.end();
MySqlConnection.destroy();
};
module.exports = MySqlOperator;
and i use it like that
const MySqlOperator = require("./src/mysql/MySql");
// These three fields are all char types
const sql = "INSERT INTO users(userUuid, userName, userPwd) values(1111,2222,3333);";
MySqlOperator(sql, {}, (error, result, fliedValues) => {
if (error) {
console.log(error.message);
} else {
/* here is undefined... */
console.log(result, fliedValues);
}
});
App.listen(ServerPort, () => {
// eslint-disable-next-line no-undef
console.log("start success");
});
After I executed node index.js, node told me that he had successfully started, and terminal output start success, but the MySQL plug-in did not work and there was no error message.What can I do to successfully insert this data into MySQL?
Firstly, you say this:
// These three fields are all char types
const sql = "INSERT INTO users(userUuid, userName, userPwd) values(1111,2222,3333);";
So I would advise you to respect types and modified this as:
const sql = "INSERT INTO users(userUuid, userName, userPwd) values('1111','2222','3333');";
And I think you have also a mistake in this part of /src/mysql/index.js file:
try {
callback && callback(result, fieldValues);
} catch {
throw new Error(error.message);
}
Your original callback function accepts three arguments: (error, result, fliedValues) and you pass there your DB result as an error and because I suppose your query result will be without error (as undefined), then console.log(result, fliedValues); writes undefined.
exports.saveUserInterfaceConfig = function(req,res){
var body = req.body;
console.log('body:['+JSON.stringify(body)+']');
var mysql = require('mysql');
var UiConfigId = [];
var connection = getDBConnection();
if(body && connection){
connection.beginTransaction(function(err){
if (err) {
/*var errorObj = {error:{code:0, text:'backend error'}};
return res.json(200, errorObj);*/
throw err;
}
var companyId = body.companyId;
var moduleId = body.moduleId;
var submoduleId = body.submoduleId;
var formfieldsId = body.formfieldsId;
for(var index3 in formfieldsId){
var UIConfigInfo = {Company_CompanyId: companyId, Modules_ModuleId: moduleId, SubModule_SubModuleId: submoduleId, SubmoduleFieldConfig_SubmoduleFieldConfigId: formfieldsId[index3]};
var saveUIConfigQuery = 'INSERT INTO ui_config SET ?';
connection.query(saveUIConfigQuery, UIConfigInfo, function(err, result) {
if (err) {
return connection.rollback(function() {
throw err;
});
}
UiConfigId.push(result.insertId);
console.log('result:['+JSON.stringify(result)+']');
connection.commit(function(err) {
if (err) {
return connection.rollback(function() {
connection.end(function(err) {
// The connection is terminated now
});
throw err;
});
} else {
connection.end(function(err) {
// The connection is terminated now
});
}
return res.json(200,{UiConfigId: UiConfigId});
console.log('UiConfigId:['+JSON.stringify(UiConfigId)+']');
console.log('success!');
// connection.release();
});
})
}
})
}
}
I have the above in my Node API. I have to execute same query in loop more than once . but im facing an issue placing the return statement for which im getting the below error.
Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:335:11)
How do I fix it?
you are calling res.json multiple times in a loop, that is the reason you are getting that error..
In Simple Words., This type of error will get when you pass statements or something after sending response.
for example:
res.send("something response");
console.log("jhgfjhgsdhgfsdf");
console.log("sdgsdfhdgfdhgsdf");
res.send("sopmething response");
it generates, what the error u got.!! Beccoz once the response have been sent, the following res.send Will not be executed..because, we can send response only once per a request.!!
for this you need to use the callbacks.
Good Luck
The reason you are getting that error is because you are calling res.json multiple times in a loop.
First of all, you should be using a callback mechanism to execute the query in a loop. For'ing over it can mess up by executing multiple queries before even the others are finished.
And coming to the response, it also should be done through a callback based on a condition. Condition can be to check whether you have finished all the queries successfully.
Here is a page with good info on exactly what you need:
https://mostafa-samir.github.io/async-iterative-patterns-pt1/
I have a python background and is currently migrating to node.js. I have problem adjusting to node.js due to its asynchronous nature.
For example, I am trying to return a value from a MySQL function.
function getLastRecord(name)
{
var connection = getMySQL_connection();
var query_str =
"SELECT name, " +
"FROM records " +
"WHERE (name = ?) " +
"LIMIT 1 ";
var query_var = [name];
var query = connection.query(query_str, query_var, function (err, rows, fields) {
//if (err) throw err;
if (err) {
//throw err;
console.log(err);
logger.info(err);
}
else {
//console.log(rows);
return rows;
}
}); //var query = connection.query(query_str, function (err, rows, fields) {
}
var rows = getLastRecord('name_record');
console.log(rows);
After some reading up, I realize the above code cannot work and I need to return a promise due to node.js's asynchronous nature. I cannot write node.js code like python. How do I convert getLastRecord() to return a promise and how do I handle the returned value?
In fact, what I want to do is something like this;
if (getLastRecord() > 20)
{
console.log("action");
}
How can this be done in node.js in a readable way?
I would like to see how promises can be implemented in this case using bluebird.
This is gonna be a little scattered, forgive me.
First, assuming this code uses the mysql driver API correctly, here's one way you could wrap it to work with a native promise:
function getLastRecord(name)
{
return new Promise(function(resolve, reject) {
// The Promise constructor should catch any errors thrown on
// this tick. Alternately, try/catch and reject(err) on catch.
var connection = getMySQL_connection();
var query_str =
"SELECT name, " +
"FROM records " +
"WHERE (name = ?) " +
"LIMIT 1 ";
var query_var = [name];
connection.query(query_str, query_var, function (err, rows, fields) {
// Call reject on error states,
// call resolve with results
if (err) {
return reject(err);
}
resolve(rows);
});
});
}
getLastRecord('name_record').then(function(rows) {
// now you have your rows, you can see if there are <20 of them
}).catch((err) => setImmediate(() => { throw err; })); // Throw async to escape the promise chain
So one thing: You still have callbacks. Callbacks are just functions that you hand to something to call at some point in the future with arguments of its choosing. So the function arguments in xs.map(fn), the (err, result) functions seen in node and the promise result and error handlers are all callbacks. This is somewhat confused by people referring to a specific kind of callback as "callbacks," the ones of (err, result) used in node core in what's called "continuation-passing style", sometimes called "nodebacks" by people that don't really like them.
For now, at least (async/await is coming eventually), you're pretty much stuck with callbacks, regardless of whether you adopt promises or not.
Also, I'll note that promises aren't immediately, obviously helpful here, as you still have a callback. Promises only really shine when you combine them with Promise.all and promise accumulators a la Array.prototype.reduce. But they do shine sometimes, and they are worth learning.
I have modified your code to use Q(NPM module) promises.
I Assumed your 'getLastRecord()' function that you specified in above snippet works correctly.
You can refer following link to get hold of Q module
Click here : Q documentation
var q = require('q');
function getLastRecord(name)
{
var deferred = q.defer(); // Use Q
var connection = getMySQL_connection();
var query_str =
"SELECT name, " +
"FROM records " +
"WHERE (name = ?) " +
"LIMIT 1 ";
var query_var = [name];
var query = connection.query(query_str, query_var, function (err, rows, fields) {
//if (err) throw err;
if (err) {
//throw err;
deferred.reject(err);
}
else {
//console.log(rows);
deferred.resolve(rows);
}
}); //var query = connection.query(query_str, function (err, rows, fields) {
return deferred.promise;
}
// Call the method like this
getLastRecord('name_record')
.then(function(rows){
// This function get called, when success
console.log(rows);
},function(error){
// This function get called, when error
console.log(error);
});
I am new to Node.js and promises. I was searching for a while for something that will meet my needs and this is what I ended up using after combining several examples I found. I wanted the ability to acquire connection per query and release it right after the query finishes (querySql), or to get a connection from pool and use it within Promise.using scope, or release it whenever I would like it (getSqlConnection).
Using this method you can concat several queries one after another without nesting them.
db.js
var mysql = require('mysql');
var Promise = require("bluebird");
Promise.promisifyAll(mysql);
Promise.promisifyAll(require("mysql/lib/Connection").prototype);
Promise.promisifyAll(require("mysql/lib/Pool").prototype);
var pool = mysql.createPool({
host: 'my_aws_host',
port: '3306',
user: 'my_user',
password: 'my_password',
database: 'db_name'
});
function getSqlConnection() {
return pool.getConnectionAsync().disposer(function (connection) {
console.log("Releasing connection back to pool")
connection.release();
});
}
function querySql (query, params) {
return Promise.using(getSqlConnection(), function (connection) {
console.log("Got connection from pool");
if (typeof params !== 'undefined'){
return connection.queryAsync(query, params);
} else {
return connection.queryAsync(query);
}
});
};
module.exports = {
getSqlConnection : getSqlConnection,
querySql : querySql
};
usage_route.js
var express = require('express');
var router = express.Router();
var dateFormat = require('dateformat');
var db = require('../my_modules/db');
var getSqlConnection = db.getSqlConnection;
var querySql = db.querySql;
var Promise = require("bluebird");
function retrieveUser(token) {
var userQuery = "select id, email from users where token = ?";
return querySql(userQuery, [token])
.then(function(rows){
if (rows.length == 0) {
return Promise.reject("did not find user");
}
var user = rows[0];
return user;
});
}
router.post('/', function (req, res, next) {
Promise.resolve().then(function () {
return retrieveUser(req.body.token);
})
.then(function (user){
email = user.email;
res.status(200).json({ "code": 0, "message": "success", "email": email});
})
.catch(function (err) {
console.error("got error: " + err);
if (err instanceof Error) {
res.status(400).send("General error");
} else {
res.status(200).json({ "code": 1000, "message": err });
}
});
});
module.exports = router;
I am still a bit new to node, so maybe I missed something let me know how it works out. Instead of triggering async node just forces it on you, so you have to think ahead and plan it.
const mysql = require('mysql');
const db = mysql.createConnection({
host: 'localhost',
user: 'user', password: 'password',
database: 'database',
});
db.connect((err) => {
// you should probably add reject instead of throwing error
// reject(new Error());
if(err){throw err;}
console.log('Mysql: Connected');
});
db.promise = (sql) => {
return new Promise((resolve, reject) => {
db.query(sql, (err, result) => {
if(err){reject(new Error());}
else{resolve(result);}
});
});
};
Here I am using the mysql module like normal, but instead I created a new function to handle the promise ahead of time, by adding it to the db const. (you see this as "connection" in a lot of node examples.
Now lets call a mysql query using the promise.
db.promise("SELECT * FROM users WHERE username='john doe' LIMIT 1;")
.then((result)=>{
console.log(result);
}).catch((err)=>{
console.log(err);
});
What I have found this useful for is when you need to do a second query based on the first query.
db.promise("SELECT * FROM users WHERE username='john doe' LIMIT 1;")
.then((result)=>{
console.log(result);
var sql = "SELECT * FROM friends WHERE username='";
sql = result[0];
sql = "';"
return db.promise(sql);
}).then((result)=>{
console.log(result);
}).catch((err)=>{
console.log(err);
});
You should actually use the mysql variables, but this should at least give you an example of using promises with mysql module.
Also with above you can still continue to use the db.query the normal way anytime within these promises, they just work like normal.
Hope this helps with the triangle of death.
You don't need to use promises, you can use a callback function, something like that:
function getLastRecord(name, next)
{
var connection = getMySQL_connection();
var query_str =
"SELECT name, " +
"FROM records " +
"LIMIT 1 ";
var query_var = [name];
var query = connection.query(query_str, query_var, function (err, rows, fields) {
//if (err) throw err;
if (err) {
//throw err;
console.log(err);
logger.info(err);
next(err);
}
else {
//console.log(rows);
next(null, rows);
}
}); //var query = connection.query(query_str, function (err, rows, fields) {
}
getLastRecord('name_record', function(err, data) {
if(err) {
// handle the error
} else {
// handle your data
}
});
Using the package promise-mysql the logic would be to chain promises using then(function(response){your code})
and
catch(function(response){your code}) to catch errors from the "then" blocks preceeding the catch block.
Following this logic, you will pass query results in objects or arrays using return at the end of the block. The return will help passing the query results to the next block. Then, the result will be found in the function argument (here it is test1). Using this logic you can chain several MySql queries and the code that is required to manipulate the result and do whatever you want.
the Connection object is created to be global because every object and variable created in every block are only local. Don't forget that you can chain more "then" blocks.
var config = {
host : 'host',
user : 'user',
password : 'pass',
database : 'database',
};
var mysql = require('promise-mysql');
var connection;
let thename =""; // which can also be an argument if you embed this code in a function
mysql.createConnection(config
).then(function(conn){
connection = conn;
let test = connection.query('select name from records WHERE name=? LIMIT 1',[thename]);
return test;
}).then(function(test1){
console.log("test1"+JSON.stringify(test1)); // result of previous block
var result = connection.query('select * from users'); // A second query if you want
connection.end();
connection = {};
return result;
}).catch(function(error){
if (connection && connection.end) connection.end();
//logs out the error from the previous block (if there is any issue add a second catch behind this one)
console.log(error);
});
To answer your initial question: How can this be done in node.js in a readable way?
There is a library called co, which gives you the possibility to write async code in a synchronous workflow. Just have a look and npm install co.
The problem you face very often with that approach, is, that you do not get Promise back from all the libraries you like to use. So you have either wrap it yourself (see answer from #Joshua Holbrook) or look for a wrapper (for example: npm install mysql-promise)
(Btw: its on the roadmap for ES7 to have native support for this type of workflow with the keywords async await, but its not yet in node: node feature list.)
This can be achieved quite simply, for example with bluebird, as you asked:
var Promise = require('bluebird');
function getLastRecord(name)
{
return new Promise(function(resolve, reject){
var connection = getMySQL_connection();
var query_str =
"SELECT name, " +
"FROM records " +
"WHERE (name = ?) " +
"LIMIT 1 ";
var query_var = [name];
var query = connection.query(query_str, query_var, function (err, rows, fields) {
//if (err) throw err;
if (err) {
//throw err;
console.log(err);
logger.info(err);
reject(err);
}
else {
resolve(rows);
//console.log(rows);
}
}); //var query = connection.query(query_str, function (err, rows, fields) {
});
}
getLastRecord('name_record')
.then(function(rows){
if (rows > 20) {
console.log("action");
}
})
.error(function(e){console.log("Error handler " + e)})
.catch(function(e){console.log("Catch handler " + e)});
May be helpful for others, extending #Dillon Burnett answer
Using async/await and params
db.promise = (sql, params) => {
return new Promise((resolve, reject) => {
db.query(sql,params, (err, result) => {
if(err){reject(new Error());}
else{resolve(result);}
});
});
};
module.exports = db;
async connection(){
const result = await db.promise("SELECT * FROM users WHERE username=?",[username]);
return result;
}
For some reason I am getting the Process exited before completing request error.
Here is my code:
var http = require('http');
var aws = require('aws-sdk');
var ddb = new aws.DynamoDB();
function getUser(userid) {
var q = ddb.getItem({
TableName: "clients",
Key: {
ClientID: { S: userid } }
}, function(err, data) {
if (err) {
console.log(err);
return err;
}
else {
console.log(data);
}
});
//console.log(q);
}
exports.handler = function(event, context) {
getUser('user23');
console.log("called DynamoDB");
};
After googling a few people suggested changing the time out to a higher amount. Which I did to one minute.
However the function only took:
Duration : 2542.23 ms
I have also checked and double checked the table name and the key name etc...
The console log has this :
2016-03-21T04:09:46.390Z - Received event
2016-03-21T04:09:46.751Z - called DynamoDB
2016-03-21T04:09:47.012Z - {}
END RequestId: id123
Can anyone see why this is not working?
Edit
As per the answer below I tried:
console.log('Loading event');
var AWS = require('aws-sdk');
var dynamodb = new AWS.DynamoDB({apiVersion: '2012-08-10'});
exports.handler = function(event, context) {
console.log(JSON.stringify(event, null, ' '));
dynamodb.listTables(function(err, data) {
console.log(JSON.stringify(data, null, ' '));
});
var tableName = "clients";
var datetime = new Date().getTime().toString();
dynamodb.getItem({
TableName: tableName,
Key: {
ClientID: { S: "gr5f4sgnca25hki" } }
}, function(err, data) {
if (err) {
context.done('error','putting item into dynamodb failed: '+err);
}
else {
context.done(data);
}
});
};
but now my response is:
"errorMessage": "[object Object]"
What I am trying to do is this: Check if Item exists in database. Get the parameters from the entry if exists, then do something with the parameters
Can anyone help me?
First of all, context.done expects an Error object as the first argument, not a string containing the word "error".
Second, if the Error object is null or undefined, then the termination will be taken as a succeed.
Now, consider your callback function:
function (err, data)
{
if (err) {
context.done('error', 'putting item into dynamodb failed: ' + err);
}
else {
context.done(data);
}
}
If you have an error, then your lambda will terminate in a failure, which is expected, but the errorMessage you'll get would simply be "error", which isn't much informative.
If you don't have an error, then your lambda will also terminate in a failure, because you are passing in data as the first argument to context.done, and remember that the first argument is always the Error object.
To fix this, you can simply do:
function (err, data)
{
if (err) {
context.done(err);
} else {
context.done(null, data);
}
}
Or even better:
function (err, data)
{
context.done(err, data);
}
If you don't want to handle the item and just return it immediately, you can use context.done as your callback function to the DynamoDB operation:
dynamodb.getItem({
TableName: tableName,
Key: {
ClientID: { S: "gr5f4sgnca25hki" }
}
}, context.done);
You need to signal Lambda end of function.
Important
To properly terminate your Lambda function execution, you must call context.succeed(), context.fail(), or context.done() method. If you don't, either your Lambda function will continue to run until the Node.js event queue is empty, or your Lambda function times out.
Here is an example:
https://gist.github.com/markusklems/1e7218d76d7583f1f7b3
"errorMessage": "[object Object]"
can be solved by a small change as follows
function(err, data) {
if (err) {
context.done(err);
}
else {
context.succeed(data);
}
});
Note where context.succeed differs() from context.done()
I'm following the tutorial but first i got this error:
deprecated: connect() is now done automatically
I changed my code but after that im faceing this error:
object is not a function at Client.CALL_NON_FUNCTION_AS_CONSTRUCTOR (native)
and I've no idea as I'm new to nodejs and mysql so kindly help me out!!
here is my code:
var Client = require('mysql').createClient({'host':'localhost','port':'3306','user':'root', 'password':''});
var client = new Client();
console.log('Connecting to mysql..');
ClientConnectionReady(client);
ClientConnectionReady = function(client){
client.query('USE login',function(err,result){
if(err){
console.log('cant create Table Error:'+ err.message);
client.end();
return;
}
ClientReady(client);
};
ClientReady = function(client){
var values = ['fahad','tariq'];
client.query('INSERT into Login SET username = ?, password = ?',values,
function(err,result){
if(err){
console.log("ClientReady Error:" + error.message);
client.end();
return;
}
console.log('inserted:'+result.affectedRows+'ros.');
console.log('Id inserted:'+ result.insertId);
});
GetData(client);
}
GetData = function(client){
client.query(
'SELECT * FROM Login',
function selectCb(err,result,fields){
if(err) {
console.log('Getdata Error'+err.message);
client.end();
return;
}
if(result.length>0) {
var firstResult = results[0];
console.log('username' + firstResult['username']);
console.log('password'+firstResult['password']);
}
});
client.end();
console.log('connection closed');
};
Not sure this is what's causing your specific message, but in ClientReady you're calling GetData before your INSERT has had a chance to execute; you should be doing it inside the callback from the INSERT.
UPDATE: Now that someone was kind enough to properly indent everything (hint: stuff that should be indented should begin with 4 spaces) it becomes clear that you have a similar problem in GetData(): you're calling client.end() before your callback has had a chance to execute. Thus your SELECT callback is trying to use a closed MySQL connection, which is probably causing the failure.