I have a simple node-based API, which needs to parse some JSON, save some data into Postgres, and then, send the appropriate response code (like http 201).
My code looks like this:
router.route('/customer')
.post(function(req, res) {
Customers = req.body;
var numberOfCustomers = Customers.length;
for(var i = 0; i < Customers.length; i++){
Customer = Customers[i];
console.log(Customer.Name + " " + Customer.Address);
var date = moment(new Date()).unix();
client.query(
'INSERT into customer (name, address, date_modified) VALUES($1, $2, $3) RETURNING id',
[Customer.Name, Customer.Address, date],
function(err, result) {
if (err) {
console.log(err);
status = 1;
} else {
console.log('row inserted with id: ' + result.rows[0].id);
if(numberOfCustomers === i) {
res.status(201).send({ message: "created" });
}
}
});
}
})
I'm getting this error:
_
http_outgoing.js:344
throw new Error('Can\'t set headers after they are sent.');
^
Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:344:11)
I need to account for the fact, that I'm performing my Postgres insert multiple times within a loop, so I can not send my response headers after just the first insert is done.
What is the most appropriate place within my 'POST' handler to put my res.status(201).send({ message: "created" });
?
Architectural decisions aside (for example, you might want a separate module that acts as an HTTP adapter to handle logic for sending response codes as opposed to doing it inside of your route controller), you can use promises to wait for all the inserts to finish and then send a single response code. For example, something like this:
var Promise = require('bluebird');
var query = Promise.promisify(client.query);
router.route('/customer')
.post(function(req, res) {
// all your logic, and then
return Promise.all(Customers.map(function() {
return query(sql, [Customer.Name, Customer.Address, date]);
})
.then(function() {
res.status(201).send({ message: 'Created' });
});
});
Check out the the bluebird docs for the API used in this example.
I'm unfamiliar with Postgres's API, but the concept should be similar: you need to wait for all the requests to your DB to be resolved first.
As stated above: Yes, async helpers such as Promises and async are beneficial for such matters. However, I do believe the 'best' way to solve this problem is to only use a single query. Instead of only performing one insert per query, batch them all up in to a single query like so:
INSERT into customer (name, address, date_modified)
VALUES
($1, $2, $3),
($4, $5, $6),
($7, $8, $9),
...
RETURNING id'
Suggestion
router.route('/customer').post(function(req, res) {
//Fetch customers
var customers = req.body;
//Store parameters and query inserts for db-query.
var params = [];
var inserts = [];
//For each customer
// - Add parameters for query
// - Build insert string
customers.forEach(function(customer){
inserts.push(
[
"($",
params.push(customer.Name),
", $",
params.push(customer.Address),
", ",
NOW(), //unnecessary to generate timestamp in js
")",
].join('')
)
});
//Build query
var query = 'INSERT into customer (name, address, date_modified) VALUES '+ inserts +' RETURNING id';
//Query database in a more simple fashion.
client.query(query, params, function(err, result) {
if (err) {
console.log(err);
status = 1;
} else {
res.status(201).send({ message: "created" });
});
}
})
If you're using ES6 you are able to simplify the string build operations by using string templating.
customers.forEach(function(customer){
var query = `($${params.push(customer.Name)}, $${params.push(customer.Address)}, NOW())`
inserts.push(query);
});
//and
var query = `
INSERT into customer (name, address, date_modified)
VALUES ${inserts}
RETURNING id
`;
The proper way be to do, I would also recommend you look into Async or lodash lib.
router.route('/customer')
.post(function(req, res) {
var Customers = req.body,
numberOfCustomers = Customers.length;
for(var i = 0; i < Customers.length; i++){
var Customer = Customers[i];
console.log(Customer.Name + " " + Customer.Address);
var date = moment(new Date()).unix(),
sql = 'INSERT into customer (name, address, date_modified) VALUES($1, $2, $3) RETURNING id';
client.query(sql, [Customer.Name, Customer.Address, date],
function(err, result) {
if (err) {
console.log(err);
res.status(500).json({message: "Server Error", err: err});
} else {
console.log('row inserted with id: ' + result.rows[0].id);
if (numberOfCustomers === i) {
res.status(201).send({ message: "Created" });
}
}
});
}
})
Related
I am trying to make an update request using Jwt (Tokens) and Node.Js with a backend in mysql.
It just tells me in Postman that the record has been updated, i try to see where the update took place and i could not find a thing. Nothing updated.
My code is looking thus :
app.put('/api/v1/logistics/update_profile', async function (req, res, next) {
try {
if (
!req.headers.authorization ||
!req.headers.authorization.startsWith('Bearer ') ||
!req.headers.authorization.split(' ')[1]
) {
return res.status(422).json({ message: 'Please Provide Token!' });
}
const theToken = req.headers.authorization.split(' ')[1];
const decoded = jwt.verify(theToken, 'fasta-logistics');
var fullname = req.body.fullname;
var email = req.body.email;
var state = req.body.state;
var city = req.body.city;
var phone_num = req.body.phone_num;
var company_type = req.body.company_type;
var company_name = req.body.company_name;
var company_regnum = req.body.company_regnum;
var l_licensenumber = req.body.l_licensenumber;
var company_address = req.body.company_address;
dbConn.query(
'UPDATE XXXXXx SET fullname =? , email =?, state=?, city=?, phone_num=?, company_type=?, company_name =?, company_regnum =?, l_licensenumber =?, company_address =? where id =?',
[
fullname,
email,
state,
city,
phone_num,
company_type,
company_name,
company_regnum,
l_licensenumber,
company_address,
decoded.id,
],
function (error, results, fields) {
if (error) throw error;
return res.send({
error: false,
data: results,
message: 'User Updated Successfully',
});
}
);
} catch (err) {
next(err);
}
});
Why does it tell me the records has been updated and nothing happens? Please I need guidance here of some sort.
return res.json({ error: false, data: results, message: 'User Updated Successfully' }); should work.
To be honest, I don't know how the "dbConn.query" works, but I guess you misspelled the arguments order or something like that. I can suggest you to use ORMs instead (at least query builders like knex), because they have declarative and more readable code, huge doc and community and issues like this doesn't exist in there.
Im using NodeJs to fetch data from mysql database, from the result object/array i want to perform specific action (call web service) with data returned from mysql. unfortunately whenever i iterate through mysql result and call web service only one object from the mysql resultset is called against web service. Here are my codes.
app.get('/blt', function(req, res){
var sql = "SELECT dest, amt FROM dit WHERE dest != 'DEST' && amt != 'AMT'";
pool.getConnection(function(err, conn){
if(err){
console.log("MYSQL Connection Error: "+err);
}
else{
conn.query(sql, function(err2, result){
if(err2){
console.log("Some error: "+err2);
}
else{
//console.log(JSON.stringify(result));
Object.keys(result).forEach(function(key) {
let result_ = result[key];
let sn = Math.floor((Math.random() * 900000000) + 100000000);
let dacc_ = result_.dest;
console.log(dacc_);
let blDetail = {
"BalDto": {
"Src": "XXXXXX",
"Code": 1,
"Amt": 10,
"Dac": dacc_, //Variable from mysql resultset
"Cmt": "Ok",
"SN": sn
}
};
soap.createClient(url, function(err, client) {
client.addSoapHeader(AuthHeader);
client.BalTransfer(blDetail, function(err, result) {
if (err) {
console.log(err.body);
} else {
console.log("done");
}
});
});
}
);
}
conn.release(function(errs){
if(errs){
console.log(errs)
}
});
});
}
});
});
I tried to use async but still i got the same results, only one of the returned mysql result set is sent to web service.
I want all the result set to be sent to webservice one by one, Kindly Help
You're literally iterating through your response from MySQL, which is a list of something, then you're making the soap call on every single one of them.
What you need to do is this:
Check the soap client and what they accept.
See if it accepts an array of your BalDto Objects
Utilize const listOfBalDtos = object.keys(result).map(); to do your transformation
Make the request:
soap.createClient(url, function(err, client) {
client.addSoapHeader(AuthHeader);
client.BalTransfer(listOfBalDtos);
});
try something like this.
I am brand new to NodeJS and am struggling to find a solution to making a reusable function to execute a query (passed via a parameter) and then simply return the response to the caller. I want to do it this way as there will be over 100 functions requiring database queries and it'll help reduce code size.
I’ve tried the following as a very primitive test, but the dbA_Read_Data() function always returns undefined, because it's asynchronous. However, 6 days later, and over 15 alternative callback/promise solutions that work for some haven't worked for me and I am no closer to solving this issue and accomplishing the following.
const { Databases } = require('../../utils/enums');
var mysql = require('mysql');
function getAllUsers() {
//This function would be called when an endpoint is hit in my HapiJS system.
var myQuery = "SELECT * FROM Users WHERE Account_Status== 'ACTIVE' ";
var response = dbA_Read_Data(myQuery);
return response;
}
//dbA => website database, contains users/permissions/etc
//Other databases... you get the idea
function dbA_Read_Data(query) {
dbConn = createConnection("localhost", Databases.db_A.name, Databases.db_A.username, Databases.db_A.password);
dbConn.connect();
dbConn.query(query, function(err, rows, fields) {
if (err) throw err;
var myResponseObject = {
query: query,
data: {
count: rows.length,
records: rows
}
}
});
}
function createConnection(host, database, user, password) {
var connection = mysql.createConnection({
host: host,
user: user,
password: password,
database: database
});
return connection;
}
How do I get the function dbA_Read_Data(query) to return the response object to the caller function?
As in the comments section, the database call conn.query is asynchronous so it is impossible to simply return the result. In order to obtain the result of this query and operate on it, you could use a Promise.
I suppose your dbA_Read_Data(query) function could look like that:
function dbA_Read_Data(query){
var dbConn = createConnection("localhost", Databases.db_A.name, Databases.db_A.username, Databases.db_A.password);
dbConn.connect();
return new Promise(function(resolve, reject){
dbConn.query(query, function(err, rows, fields) {
if ( err ) {
reject(err);
} else {
var myResponseObject = {
query: query,
data: {
count: rows.length,
records: rows
}
}
resolve(myResponseObject);
}
});
});
}
Then your getAllUsers() function call would require .then() in order to obtain the results:
function getAllUsers(){
var myQuery = "SELECT * FROM Users WHERE Account_Status== 'ACTIVE' ";
return dbA_Read_Data(myQuery).then(function(result){
return result;
}).catch(function(error){
// handle the error properly here...
console.log('error in database operation');
});
}
getAllUsers().then(function(users){
// do something with the result...
console.log(users);
});
I wrote a code for running insert queries in parallel in Node.js and I am also using Promise.js.
But the code fails and raises an exception of "Duplicate Primary Key" entry.
The code is as follows,
var Promise = require("promise");
var mySql = require("mysql");
var _ = require("underscore");
var connection = mySql.createConnection({
host : "localhost",
user : "root",
password : "rahul",
database : "testDb" //schema
});
connection.connect();
function insertDept(name){
return new Promise(fn);
function fn(resolve,reject){
getMaxDept().then(function(rows){
var deptId = rows[0]["DeptId"];
deptId = (_.isNull(deptId) === true) ? 125 : deptId;
var sql = "insert into departmentTbl values("+deptId+",'"+name+"')";
console.log(sql);
connection.query(sql,function(err,rows,fields){
if(err){
console.log(err);
return reject(err);
}else{
return resolve(rows);
}
});
}).catch(function(error){
return reject(error);
});
}//fn
}//insertDept
function getMaxDept(){
return new Promise(fn);
function fn(resolve,reject){
var sql = "select max(deptId) + 1 as 'DeptId' from departmentTbl";
connection.query(sql,function(err,rows,fields){
if(err){
console.log(err.stack);
return reject(err);
}else{
return resolve(rows);
}
});
}// fn
} //getMaxDept
function createDeptForAll(){
var promiseObj = [];
if(arguments.length > 0){
_.each(arguments,callback);
}else{
throw "No departments passed";
}
function callback(deptName){
promiseObj.push(insertDept(deptName))
}
return Promise.all(promiseObj);
}//createDeptForAll
createDeptForAll("Archiology","Anthropology").then(function(createDepartment){
createDepartment.then(function(rows){
console.log("Rows inserted "+rows["affectedRows"]);
}).catch(function(error){
console.log(error);
}).done(function(){
connection.end();
});
});
When I run the above code code,
the output is
rahul#rahul:~/myPractise/NodeWebApp/NodeMySqlv1.0$ node queryUsingPromise02.js
insert into departmentTbl values(125,'Archiology')
insert into departmentTbl values(125,'Anthropology')
{ [Error: ER_DUP_ENTRY: Duplicate entry '125' for key 'PRIMARY'] code: 'ER_DUP_ENTRY', errno: 1062, sqlState: '23000', index: 0 }
As Department Id is Primary key and the promises run in parallel,
the primary key for second Department's insert query fails.
As you can see, before any insert query, I fetch the max of departments + 1.
if the above query fails, I assign '125'.
Now, what should I change so that my above written code runs.
Should I use trigger of "before insert" for calculating next value of primary key of "department ID" at the database level itself or should I do something in my own Node.js code?
This issue is not restricted to node or JavaScript, but you will face this problem with any technology that tries to write to an SQL database in parallel. Unique id generation in a scenario like this is not trivial.
If you have the option to do so, make your id field in your database AUTO_INCREMENT, this will save you a lot of headaches in situations like this.
More about AUTO_INCREMENT.
Advice on AUTO_INCREMENT looks good.
You might also consider writing a promisifier for connection.query(), allowing for the remaining code to be tidied up.
So with getMaxDept() purged and a connection.queryAsync() utility in place, you might end up with something like this :
var Promise = require("promise");
var mySql = require("mysql");
var connection = mySql.createConnection({
host: "localhost",
user: "root",
password: "rahul",
database: "testDb" //schema
});
connection.connect();
// promisifier for connection.query()
connection.queryAsync = function(sql) {
return new Promise((resolve, reject) => {
connection.query(sql, (err, rows, fields) => {
if(err) { reject(err); }
else { resolve({'rows':rows, 'fields':fields}); }
});
});
};
function insertDept(name) {
var sql = "insert into departmentTbl values(" + name + "')"; // assumed - needs checking
return connection.queryAsync(sql);
}
function createDeptForAll(departments) {
if(departments.length > 0) {
return Promise.all(departments.map(insertDept));
} else {
return Promise.reject(new Error('No departments passed'));
}
}
createDeptForAll(['Archiology', 'Anthropology']).then((results) => {
results.forEach((result) => {
console.log("Rows inserted " + result.rows.affectedRows);
connection.end();
});
}).catch((error) => {
console.log(error);
connection.end();
});
I am some cofused by asychronous nodejs and mongoose. Simplily, I want to post an array of usernames and check, if a username is in database, then I put it in the valid array, otherwise, put it in the invalid array.
Here is my current code:
var User = require('../../db/models/user');
api.post('/userlist', function(req, res) {
var invalid = []; // usernames which can not be found in database
var valid = []; // usernames which can be found in database
(req.body.userlist).forEach(function(username) {
User
.findOne({username: username})
.exec(function(err, user) {
if (err) {
res.send(err);
return;
} else if (!user) {
invalid.push(username);
} else {
valid.push(req.params.item);
}
});
});
res.send({
Invalid: invalid,
Valid: valid
});
});
When I executed the above code, it outputs the intial empty array directly.
Invalid: [],
Valid: []
I know it is because nodejs first execute this res.send then execute function .exec(function(err, user), but i do not know how to get the right invalid and valid array, pls advise.
Your best bet is to use a promise:
api.post('/userlist', (req, res) => {
// Takes a username and returns a promise for information on that username.
function findByUsername(username) {
return new Promise((resolve, reject) =>
User.findOne({username}).exec((err, user) =>
err ? reject(err) : resolve(user)
)
);
}
// Iterate the array and transform each user to a promise for data on that user.
Promise.all(req.body.userlist.map(findByUsername))
// Then, when all of the promises in that new array resolve
.then(allUserDataInOrder => {
// Find all the valid ones (if (user))
let Valid = allUserDataInOrder.filter(Boolean); // Only those who are truthy
// And all the invalid ones (if (!user))
let Invalid = allUserDataInOrder.filter(userData => !userData); // Sadly, no convenient function here :(
// And send both away
res.send({Valid, Invalid}); // Short syntax FTW!
})
.catch(res.send); // Called with error object if any.
});
While these other solutions solve what you're trying to accomplish, they still incorporate bad design by iterating findOne(). Executing 1 query for every item in your list is incredibly inefficient. Using an $in query and a basic map, you can use a single query:
var User = require('../../db/models/user');
api.post('/userlist', function(req, res) {
User.find({username: {$in: req.body.userlist}}, function(err, users) {
if (err) {
return res.send(err);
}
// create a map of all the users in your list that exist in your database
var dbUserMap = {};
users.forEach(function(user) {
dbUserMap[user.username] = true;
});
var valid = [];
var invalid = [];
// check your POST list against the database map
req.body.userlist.forEach(function(username){
if (dbUserMap[username]) {
valid.push(username);
}
else {
invalid.push(username);
}
});
res.send({
valid: valid,
invalid: invalid
});
});
});
Try to use async module:
var invalid = [];
var valid = [];
async.each(req.body.userlist, function(name, next) {
User.findOne({username: name}, function(err, user) {
if (err) {
return next(err);
}
if (!user) {
invalid.push(name);
} else {
valid.push(name);
}
next();
)};
}, function(err) {
if (err) {
return res.send(err);
}
res.send({
Invalid: invalid,
Valid: valid
});
});