Node mysql bulk insert with express array parameter - mysql

I've come across a situation where I need to use a bulk insert with my Node project.
This of course has already been answered here: How do I do a bulk insert in mySQL using node.js
However, I have an express project which I use to create an api. The parameters are turned into an array and I'm having trouble using that array with a bulk insert. Whenever I try to use that route, I get an error of Error: ER_WRONG_VALUE_COUNT_ON_ROW: Column count doesn't match value count at row 1
After some digging I found that it tries to insert:
['foo', 'bar', 'test']
When I need it to insert:
['foo']
['bar']
['test']
Anyways, here's the whole code:
Route
router.post("/", function (req, res, next) {
db.query(
"REPLACE INTO user (`Name`) VALUES (?)",
[req.query.array],
function (error, response) {
if (error) throw error;
console.log(response);
}
)
});
Route Caller
let requestUrl = "http://localhost:3000/user?";
// External api request which returns a list of users
for (let i = 0; i < body.users.length; i++) {
requestUrl += `array=${body.users[i]}&`
}
let addUserRequest = {
url: requestUrl,
method: "POST"
};
request(addUserRequest, function (error, response, body) {
console.log(body);
});
The url that is generated is:
http://localhost:3000/user?array=foo&array=bar&array=test

Try this,
var datatoDB = [];
req.query.array.forEach(function(entry) {
console.log(entry);
datatoDB.push([entry]);
});
Here we are trying to convert this ['foo', 'bar', 'test'] to this [["foo"], ["bar"], ["test"]].
Now, use datatoDB in your function.
router.post("/", function (req, res, next) {
db.query(
"REPLACE INTO user (Name) VALUES ?",
[datatoDB],
function (error, response) {
if (error) throw error;
console.log(response);
}
)
});

Related

having issues calling a sql stored procedure, SelectById, from express

I'm trying to call a saved stored procedure from SQL in my node app. my server is connected and I am able to execute my selectRandom5 saved proc with no problems.
the issue I am having is when I try to do a getById where I need to declare the #Id input. I've tried a couple of variations of the function with no luck, here are two I've tried.
the error message I get with this is UnhandledPromiseRejectionWarning: RequestError: Incorrect syntax near '?'.
selectById(req, res) {
var theId = req.params.id;
// connect to your database
sql.connect(config, function (err) {
if (err) console.log(err);
// create Request object
var request = new sql.Request();
// query to the database and get the records
request.query("CALL Addresses_SelectById(?)", [theId], function (err, recordset) {
if (err) console.log("connect", err);
// send records as a response
res.send(recordset);
console.log(recordset);
});
});
}
and then there's this other function I've tried, and the error message I get from this is 'Must declare the scalar variable "#Id".'
selectById(req, res) {
var theId = req.params.id;
// connect to your database
sql.connect(config, function (err) {
if (err) console.log(err);
// create Request object
var request = new sql.Request();
// query to the database and get the records
request.query(`SET #Id = ${theId}CALL Addresses_SelectById(#Id)`, function (err, recordset) {
if (err) console.log("connect", err);
// send records as a response
res.send(recordset);
console.log(recordset);
});
});
}
I just want to be able to pass parameters to SQL to be able to create update or get by but so far I haven't been able to figure out the proper way to pass the parameters.
any help would be appreciated! thanks guys
I FOUND IT GUYS!
selectById(req, res) {
var theId = req.params.id;
// connect to your database
sql.connect(config, function (err) {
if (err) console.log(err);
// create Request object
var request = new sql.Request();
// query to the database and get the records
request.input("Id", sql.Int, theId);
request.execute("Addresses_SelectById", function (err, recordset) {
if (err) console.log("connect", err);
// send records as a response
res.send(recordset);
console.log(recordset);
});
});
I changed it to this and it works
Problem 1:
Suggested alternate syntax:
selectById(req, res) {
var theId = req.params.id;
let sql = `CALL Addresses_SelectById(?)`;
connection.query(sql, theId, (error, results, fields) => {
if (error) {
return console.error(error.message);
}
console.log(results[0]);
// Possibly stringify "results" to JSON before sending...
res.send(results);
});
}

Node and Axios - How to make sequentials requests

I need to make 2 requests to my API to insert data in 2 different table:
Workflow:
request to get the last id + 1 => create the array I need (last_id, values) => two INSERT in MySql, 1st with varius data, 2nd with the array I created.
router.post("/addentry", function (req, res) {
let sql = "SELECT MAX(id) + 1 AS last_id FROM entries;"; // I get the id
let query = connection
.query(sql, (err, results) => {
if (err) throw err;
res.header("Access-Control-Allow-Origin", "*");
// put the id in a variable
var last_id = results[0].last_id;
var categoriesMap = req.body.categories;
var valCat = Object.values(categoriesMap);
// I create the array with other data
var catArray = valCat.map((item) => {
return [last_id, item];
});
})
.then((catArray) => {
let sql = `BEGIN; INSERT INTO entries (title,kindof) VALUES("${[
req.body.title,
]}","${req.body.kindof}");
INSERT INTO categories_main (entry_id, cat_id) VALUES ? ;
COMMIT;`;
let query = connection.query(sql, [catArray], (err, results) => {
if (err) throw err;
console.log(results);
res.header("Access-Control-Allow-Origin", "*");
res.send("Entry added to DB");
});
});
The first part works perfectly but with the second I get
TypeError: connection.query(...).then is not a function
Any idea how to do it?
Thanks
First things first, you should make sure that you use node-mysql2 instead of node-mysql. node-mysql2 has a built in functionality that helps making multiple queries inside a single connection. I have provided you this answer that exemplifies how to use it properly.
Moving forward, after you've done that, to be able to work with your result object, you will need JSON.
The following syntax is what you probably want to use:
var stringify = JSON.parse(JSON.stringify(results[0]));
for (var i = 0; i < stringify.length; i++) {
var last_id = stringify[i]["last_id"];
}
I need to make 2 requests to my API to insert data in 2 different table:
From code, I see that you are intending to do a single API call to the server and run 2 queries.
You can do .then only on a Promise, so as we can see connection.query is not returning a Promise and hence not then able.
Also you are setting response headers multiple times res.header("Access-Control-Allow-Origin", "*"); do this only once in a request cycle. So lets follow the callback approach instead of then.
let sql = "SELECT MAX(id) + 1 AS last_id FROM entries;"; // I get the id
let query = connection
.query(sql, (err, results) => {
if (err) {
res.header("Access-Control-Allow-Origin", "*");
return res.status(500).send({error:'server error'});
}
// put the id in a variable
var last_id = results[0].last_id;
var categoriesMap = req.body.categories;
var valCat = Object.values(categoriesMap);
// I create the array with other data
var catArray = valCat.map((item) => {
return [last_id, item];
});
let sql = `BEGIN; INSERT INTO entries (title,kindof) VALUES("${[
req.body.title,
]}","${req.body.kindof}");
INSERT INTO categories_main (entry_id, cat_id) VALUES ? ;
COMMIT;`;
let query = connection.query(sql, [catArray], (err, results) => {
if (err) {
res.header("Access-Control-Allow-Origin", "*");
return res.status(500).send({error:'server error'});
}
console.log(results);
res.header("Access-Control-Allow-Origin", "*");
res.send("Entry added to DB");
});
})
Here the complete solution, starting from what #SubinSebastian advised to me.
First of all I needed node-mysql2, that alows promises and therefore chained requests.
And then:
router.post("/addentry", function (req, res) {
let sql = "SELECT MAX(id) + 1 AS last_id FROM entries;";
connection.promise().query(sql)
.then((results) => {
// I get the value from results
var stringify = JSON.parse(JSON.stringify(results[0]));
for (var i = 0; i < stringify.length; i++) {
console.log(stringify[i]["last_id"]);
var last_id = stringify[i]["last_id"];
}
// I get some parameters and I create the array
var categoriesMap = req.body.categories;
var valCat = Object.values(categoriesMap);
var catArray = valCat.map((item) => {
return [last_id, item];
});
let sql = `BEGIN; INSERT INTO entries (title,kindof) VALUES("${[
req.body.title,
]}","${req.body.kindof}");
INSERT INTO categories_main (entry_id, cat_id) VALUES ? ;
COMMIT;`;
// array as second query parameter
let query = connection.query(sql, [catArray], (err,results) => {
if (err) throw err;
});
})
.catch(console.log);

How to send Post request for each iteration of an array in Node/Express?

I have some raw json that I'm trying to send to my back end server in mysql. I'm currently trying to loop through the specific array in the json that I need and sending data from each of the children in the array via a POST request but I am getting "Cannot set headers after they are sent to the client".
app.post('/reddit-import', function (req, res) {
console.log("Route /reddit-import POST");
let data = req.body.data.children
data.forEach(child => {
let sql1 = `CALL insert_user('${child.data.author}',
'${child.data.author_fullname}');`
connection.query(sql1,
data,
function (errQuery, result) {
if (errQuery) {
console.log(errQuery);
res.json({status: "Error", err: errQuery});
res.end();
} else {
console.log("Insert ID: ", result.insertId);
res.json({status: result.insertId, err: ""});
res.end();
}
}
);
When I send the POST request, my backend gets 2 rows of data before it hits me with the error message...any ideas?
You seem to be ending your outer response in the data.forEach with a res.end(), which I’m assuming is used to indicate the end of the outer HTTP request to the client. Did you perhaps mean to use “result” there instead?
Try this if you need to keep track insert IDs:
app.post('/reddit-import', function(req, res) {
console.log("Route /reddit-import POST");
let data = req.body.data.children
const insertIds = data.map(child => {
return new Promise((resolve, reject) => {
const sql = `CALL insert_user('${child.data.author}', '${child.data.author_fullname}')`;
connection.query(sql, (err, result) => {
if (err) {
console.log(err);
return reject(err);
}
console.log("Insert ID: ", result.insertId);
return resolve(result.insertId);
});
});
});
return Promise.all(insertIds)
.then(ids => {
return res.json({
insertIds: ids
});
})
.catch(err => {
return res.status(500).json({
message: 'got query error'
});
});
});
What this basically does is that on each query, you keep track of the insert IDs. We need to use Promises because the query() function is asynchronous, meaning it runs independently and there's no other way to keep track of the data outside of its function(err, result) callback. Now we have an array of Promises which contains the insert IDs, and what's left is to send a response that this is successful. And in order to do that, we can't simply do res.json(insertIds) because insertIds is an array of Promises and we still need to extract the values. We can easily extract all data at once from an array of Promises by using Promise.all(insertIds).then(ids => ...). If you wish to send a response informing that the request is successful, do so in this then callback. Lastly and most importantly, we handle errors in a Promise chain's .catch() block. This is where you want to send a response informing the client that there are errors.
Some things that we can improve from this solution is to implement rollbacks in case we have errors, and of course validations of parameters. Unfortunately I have to leave this to the OP to implement.
Also, keep in mind you should only send a response once and only once each request.

API delete call for row from MySQL DB not work

I'd like to create api call from back-end for DELETE query from mysql DB but when execute it in browser get error
'Cannot GET ...'
I pass into the route id of row which had got from DB
At back-end the code is:
app.delete('/products/delete/:id*?', function(req, res) =>{
let { id } = req.query;
let DELETE_PRODUCT_FROM_DB = `DELETE FROM my_db.products WHERE my_db.id= '${req.query}'`;
console.log("id: ", req.query);
// delete a row with id = req.query
connection.query(DELETE_PRODUCT_FROM_DB, (error, results, fields) => {
if (error) return console.error(error.message);
res.status(200).send(results);
console.log("Deleted Row(s):", results.affectedRows);
});
});
But finally this call not works and row not deleted
let DELETE_PRODUCT_FROM_DB = `DELETE FROM my_db.products WHERE my_db.id= '${req.query.id}'`;
console.log("id: ", req.query.id);
Try using this.
fetch(url, {
method: 'delete'
}).then(response => response.json());
Try running this in your browser console. It should work.
Most likely you're making a GET call to a DELETE resource.
Please read Express 4.x. Can you share the code you're using to make DELETE request from browser?
I did some changes and now running version of the code looks like
app.delete('/products/delete/:id', (req, res) => {
let { id } = req.params ;
let DELETE_PRODUCT_FROM_DB = `DELETE FROM my_DB.products WHERE id= '${id}'`;
console.log('id: ', req.params);
// delete a row with id = req.params
connection.query(DELETE_PRODUCT_FROM_DB, (error, results, fields) => {
if (error) return console.error(error.message);
res.status(200).send(results);
console.log('Deleted Row(s):', results.affectedRows);
});
});
Also, I figured out that changes from req.query on req.params helped to get id from the link as a parameter

NodeJS MySQL concat queries with responses from many model queries to one controller call

I'm having problems understanding the async methods on nodejs.
I have this fragment of code in my controller:
app.get(basePath, function (req, res, next) {
model.generateDB(function (modelErr, modelRes) {
if (modelErr) console.log('Error: ' + modelErr);
next(res.send(modelRes));
});
});
this fragment of code for the model:
generateDB: function (next) {
BDManager.query(
'INSERT INTO tableName' +
'(field1, field2) VALUES ("a", "b")',
function (err, res) {
next(err, res);
});
}
and this fragment of code for the db manager
query: function (sql, next) {
var con = mysql.createConnection(config.MySQL);
con.query(sql, function (err, res) {
if (err) next(err, null);
next(null, res);
});
con.end();
}
It works fine for me. the question is how may I have multiple queries with they're responses in the model with only one controller call, like the example (that not works):
BDManager.query(
'INSERT INTO tableName' +
'(field1, field2) VALUES ("a", "b")',
function (err, res) {
next(err, res);
});
BDManager.query(
'INSERT INTO tableName' +
'(field1, field2) VALUES ("a", "b")',
function (err, res) {
next(err, res);
});
BDManager.query(
'INSERT INTO tableName' +
'(field1, field2) VALUES ("a", "b")',
function (err, res) {
next(err, res);
});
the idea could be to get an array of errors and responses, but I don't know how to send it when all queries finish. I tried with the .then, but it seems that doesn't works (The error I get using .then is "can't use then on null").
Another solution could be to concat many queries in one, but I tried with "; " separator and doesn't works for me.
So this is using callbacks (.then is for promises). You could create a promise wrapper around it that would let you promisify it and then you could await them or use promise.all if you wanted to run them in parallel.
For example:
function promiseQuery(query, params) {
return new Promise((resolve, reject) => {
BDManager.query(query, params, function (err, res) {
if (err) return reject(err);
return resolve(res);
});
});
}
let arrayOfResponses = await Promise.all([
promiseQuery(query1, params1),
promiseQuery(query2, params2),
promiseQuery(query3, params3),
]);
Just a few things about that - you probably should be inserting values via parameterized inputs. Your SQL library should support that
Also an error will throw a rejection on this. If you want to catch those errors and push them to an array, you could do that as well with a .catch handler.
If you're not using async/await or a version of node that's compatible, you can also do:
Promise.all([]).then(arrayOfResponses => {});
As well and that will give you the array of the responses for any promises you pass in to promise.all.
There are tons of articles on how to use Promises and how they work but that should get you started.