I have an express.js based rest application. Please have a look on following code and suggest me what would be better way.
I want to select user and its associated images (1 user has many images).
function getUser (connection, req, res) {
var userId = req.params.id;
connection.query('SELECT * FROM user p'
+ ' WHERE p.id = ' + connection.escape(userId), function handleSql(err, rows) {
if (err){ logAndRespond(err,res); return; }
if (rows.length === 0){ res.send(204); return; }
var adId = rows[0].adId;
// load images
connection.query('SELECT id, url FROM image WHERE ad_id = ' + connection.escape(adId), function (err, imgRows) {
if (err){ logAndRespond(err,res); return; }
if (rows.length != 0){
rows[0].images = imgRows;
}
res.json({'user': rows});
connection.release();
});
});
}
You don't have to escape parameters by yourself
You don't release the connection if an error occurred
The problem now is I don't know what you want to do with selected rows. You are also checking the rows.length twice but if there weren't any records in the first query then the second one will not be executed.
function getUser(conn, req, res) {
conn.query("SELECT * FROM user p WHERE p.id = ?;", [req.params.id], function(err, rows) {
if (err) {
return logAndRespond(err, res);
}
if (!rows.length) {
return res.send(204);
}
conn.query("SELECT id, url FROM image WHERE ad_id = ?;", [rows[0].adId], function(err, imgRows) {
if (err) {
return logAndRespond(err, res);
}
if (rows.length) { // ???
rows[0].images = imgRows;
}
res.json({"user": rows});
conn.release();
});
});
}
Related
So, i seem to be having a problem with returning a value from my forEach MYSQL query loop in Node JS and then inserting this into an ejs template. Currently, these are my pieces of code:
app.js GET Request
app.get("/admin/stock", function(req, res) {
db.query("SELECT * FROM stock", function (err, stock) {
if (err) {
console.log("Error with showing SQL")
} else {
stock.forEach(function (stock) {
db.query("SELECT * FROM product WHERE productID = " +
stock.stock_productID, function (err, product) {
if (err) {
console.log("Error");
}
else {
stock.stock_productID = product[0].productName
}
});
db.query("SELECT * FROM currency WHERE currencyID = " +
stock.stock_currencyID, function (err, currency) {
if (err) {
console.log("Error");
}
else {
stock.stock_currencyID = currency[0].currencyName
}
});
db.query("SELECT * FROM customer WHERE customerID = " +
stock.stock_customerID, function (err, customer) {
if (err) {
console.log("Error");
}
else {
stock.stock_customerID =
customer[0].customerBusiness;
console.log(stock);
}
})
});
}
console.log(stock);
res.render("admin/stock", {stock:stock, userFName: req.user[1]})
})
});
Stock Page EJS
<table class="generalTable">
<tr>
<th>Product Name</th>
<th>Quantity</th>
<th>Currency</th>
<th>Cost</th>
<th>Date Bought</th>
<th>Bought From</th>
</tr>
<% stock.forEach(function(stock){ %>
<tr>
<td><%=stock.stock_productID%></td>
<td><%=stock.quantityStockCurrent%></td>
<td><%=stock.stock_currencyID%></td>
<td><%=stock.priceBought%></td>
<td><%=stock.boughtDate%></td>
<td><%=stock.stock_customerID%></td>
</tr>
<% }) %>
</table>
The First console.log of stock shows the replaced values, the second however, shows the original array values, and therefore the page is loaded in with the original values (IDs etc, instead of names).
How do I send the inner/edited array to the res.render instead of the original array?
Many Thanks,
Brad
EDIT//////
By using JOINs, the output was correct
I guess the database queries have not yet returned when you already render the stock object.
Remember, nodejs is asynchronous. You have to wait for all DB queries to come back before rendering the result.
I did have a similar problem when I first learned about nodejs: JavaScript nodejs mysql with queries in a loop
The problem is that javascript is asynchronous and that Javascript doesn't have a block scope, which means that you render file before the loop complete so you should use simple for loop for this.
you should replace your code with this :-
app.get("/admin/stock", function(req, res) {
db.query("SELECT * FROM stock", function (err, stocks) {
var stock = JSON.parse(JSON.stringify(stocks));
if (err) {
console.log("Error with showing SQL")
} else {
for(var i = 0 ; i< stock.length; i++) {
db.query("SELECT * FROM product WHERE productID = " +
stock[i].stock_productID, function (err, product) {
if (err) {
console.log("Error");
}
else {
stock[i].stock_productID = product[0].productName
}
});
db.query("SELECT * FROM currency WHERE currencyID = " +
stock[i].stock_currencyID, function (err, currency) {
if (err) {
console.log("Error");
}
else {
stock[i].stock_currencyID = currency[0].currencyName
}
});
db.query("SELECT * FROM customer WHERE customerID = " +
stock[i].stock_customerID, function (err, customer) {
if (err) {
console.log("Error");
}
else {
stock[i].stock_customerID =
customer[0].customerBusiness;
console.log(stock);
}
});
if(i === stock.length-1){
console.log(stock);
res.render("admin/stock", {stock:stock, userFName: req.user[1]})
}
};
}
})
});
You can use async.map for executing query in loop.
function getResults(id,cb){
db.query("SELECT * FROM product WHERE productID = " +
id.stock_productID, function (err, product) {
if (err) {
console.log("Error");
}
else {
id.stock_productID = product[0].productName
}
});
}
async.mapLimit(stock, 5, function(id, callback) {
getResults(id, function (err, res) {
if (err) return callback(err);
callback(null, res);
})
}, function(err, results) {
// results is an array of names
});
I'm trying to get a quiz from table quizzes and after obtaining it
trying to an array of answers from table questions. But it doesn't work
I send questionDto(JSON) to this.add method, where the question I've already
added in the table and need to add answers and
var question = require('../models/question');
function Quiz() {
this.getById = function(quizId,res) {
connection.acquire(function(err, con) {
con.query('select * from quizes where `id` = ? ', quizId,
function(err, result) {
con.release();
result.questions = question.getAllByQuizId(quizId);
res.send(result);
});
});
};
question.getAllByQuizId(quizId); has to return an array of questions. Here is an implementation
function Question() {
this.getAllByQuizId = function(quizId) {
return connection.acquire(function(err, con) {
return con.query('select * from questions where `quizId` = ? ', quizId, function(err, rows) {
if (err) throw err;
con.release();
console.log(JSON.parse(JSON.stringify(rows)));
return JSON.parse(JSON.stringify(rows));
});
});
};
console.log() returns applicable value but the function return undefined
I have wasted hours to tackle. But still can't :(
You should pass a callback to handle this instead of 'return' as it has a asynchronous block in the function.
var question = require('../models/question');
function Quiz() {
this.getById = function(quizId,res) {
connection.acquire(function(err, con) {
con.query('select * from quizes where `id` = ? ', quizId,
function(err, result) {
con.release();
question.getAllByQuizId(quizId, function(err, data){
if(err) {
throw err;
} else {
result.questions = data;
res.send(result);
}
});
});
});
};
function Question() {
this.getAllByQuizId = function(quizId, callback) {
connection.acquire(function(err, con) {
con.query('select * from questions where `quizId` = ? ', quizId, function(err, rows) {
if (err) callback(err);
con.release();
console.log(JSON.parse(JSON.stringify(rows)));
callback(null, JSON.parse(JSON.stringify(rows)));
});
});
};
I'm new in making API. I use Node.js and MySQL.
The fact is I have two GET function to get all users and one to get user by ID.
Both function are working when they are alone implemented. If both of them are implemented the function to get all user try to enter in the function to get user by ID so the API crash.
So here is my model users.js
var connection = require("../connection");
function Users()
{
//GET ALL USERS
this.get = function(res)
{
console.log('Request without id');
connection.acquire(function(err, con)
{
con.query('SELECT * FROM users', function(err, result)
{
con.release();
if (err)
res.send({status: 1, message: 'Failed to get users'})
else
res.send(result);
});
});
}
//GET USER BY ID
this.get = function(id, res)
{
console.log('Request with ID');
connection.acquire(function(err, con)
{
if (id != null)
{
con.query('SELECT * FROM users WHERE id = ?', id, function(err, result)
{
con.release();
if (err)
res.send({status: 1, message: 'Failed to find user: ' + id});
else if (result == "")
res.send({status: 1, message: 'Failed to find user: ' + id});
else
res.send(result);
});
}
});
}
And here is the routes.js
var users = require('./models/users');
module.exports = {
configure: function(app) {
app.get('/users/', function(req, res) {
users.get(res);
});
app.get('/users/:id/', function(req, res) {
users.get(req.params.id, res);
});
Do you have any idea why ?
Thanks for help :)
You can't have two functions with the same name in the same scope.
You have to rename your functions
/**
* Get all users
*/
this.get = function(res) {...}
/**
* Get user by id
*/
this.getById = function(id, res) {...}
Or you can have one function and check if an id is provided
this.get = function(id, res) {
if ( Number.isInteger(id) ) {
// return the user
} else {
res = id;
// return all users
}
}
I use NodeJS to insert data to a table with many to many relationship, and I want to include two foreign keys when I insert data into the table this is how my code looks:
con.query("SELECT * FROM Transaction WHERE TransactionID > 1", function(err, res) {
if (err) {
throw (err);
} else if (res.length > 0) {
console.log("Transaction already exit");
} else {
var transactionID;
var filePK;
con.query("SELECT Filename FROM File WHERE Filename = ?", fileName, function(err, res) {
if (err) throw err;
filePK = JSON.stringify(res);
});
con.query("SELECT TransactionDescriptionPK FROM TransactionDescription WHERE TransactionDescriptionPK > 0", function(err, res) {
if (err) throw err;
//console.log(res);
transactionID = res;
});
var tran = {
TransactionID: data.ID,
TransactionDate: data.Description,
Amount: data.Amount
};
con.query("INSERT INTO Transaction SET ?", tran, function(err, res) {
if (err) throw err;
});
How I return the results from those queries so I can add them to the tran object?
You can use async.parallel
async.parallel([
function(callback) {
// you can directly pass the parallel callback to mysql query
// if you don't need to do anything else
return con.query(
"SELECT * FROM Transaction WHERE TransactionID > 1",
callback
);
},
function(callback) {
// otherwise, just do you what you want (here JSON.stringify)
// then don't forget to return the parallel callback
return con.query(
"SELECT Filename FROM File WHERE Filename = ?",
fileName,
function(err, res) {
if (err)
return callback(err);
const filePK = JSON.stringify(res);
return callback(null, filePK);
}
);
},
function(callback) {
return con.query(
"SELECT TransactionDescriptionPK FROM TransactionDescription WHERE TransactionDescriptionPK > 0",
callback
);
},
], function(err, data) {
// if any mysql queries above encounter an error,
// it calls the parallel final callback and stops other functions
// without errors, data looks like :
// data[0] equals mysql result object of the first query
// data[1] equals filePK const
// data[2] equals mysql result object of the last query
const tran = {
TransactionID: data[0][0].ID,
TransactionDate: data[0][0].Description,
// Amount: data.Amount // don't know where the amount come from,
// but you get the idea
};
con.query(
"INSERT INTO Transaction SET ?",
tran,
function(err, res) {
if (err)
throw err;
// ...
}
);
});
first, i connect the db and select DB:
var defaultOptions = {
user: "root",
pwd:'admin',
db:"britcham_dev_local",
server:"local", // Maybe we don't need this variable.
};
var client = new Client();
client.user = defaultOptions.user;
client.password = defaultOptions.pwd;
client.connect(function (error, results) {
//
});
client.query('USE ' + defaultOptions.db, function (error, results) {
//
});
Second, I query with client object:
var self = this;
var this.users;
client.query("SELECT * FROM users", function (error, results, fields) {
if (error) {
//
}
if (results.length > 0) {
self.users = results;
}
});
console.log(this.users);
it's nothing output ??? Why ??
Since node.js is non-blocking and asynchronous, then in this code:
client.query("SELECT * FROM users", function (error, results, fields) {
if (error) {
//
}
if (results.length > 0) {
self.users = results;
}
});
console.log(this.users);
data from DB are not probably loaded yet into users variable when you are trying to log it into console. You can check it out if you do your console.log operation within the query, for example:
client.query("SELECT * FROM users", function (error, results, fields) {
if (error) {
//
}
if (results.length > 0) {
console.log(results);
}
});
To pass the result into a variable when the operation is finished you can wrap your client DB call into a function with callback parameter and set your variable when the callback is invoked, for example:
function query(sql, callback) {
client.query(sql, function (error, results, fields) {
if (error) {
//
}
if (results.length > 0) {
callback(results);
}
});
}
query("SELECT * FROM users", function(results) {
self.users = results;
console.log(self.users);
});
Above code is just a concept.
How is the suggested answer different from this?
var self = this;
var this.users;
client.query("SELECT * FROM users", function (error, results, fields) {
if (error) {
//
}
if (results.length > 0) {
self.users = results;
console.log(this.users);
}
});
I might be wrong this is not different from the suggested answer in that it writes to console no sooner than when we have the data back from the DB.
The suggested answer seems only to add yet another function?