I'm trying to create an api route using Node and Express and I must say I don't have much experience with it. Right know I have the following code:
app.get('/api/place/:id', (req, res) => {
var id = req.params.id;
var message_error = '{"status": "failed", "message": "Unable to fetch data"}';
db.query("SELECT `id`, `user`, `lat`, `lon`, `elevation`, `rating`, `rating_count`, `country`, `continent`, `locality` FROM `t_points` WHERE id = ?", [id], (err, res1) => {
if(err) {
res.json(message_error);
} else {
//Store the user id from the points table and use it to fetch user datas
var userId = res1[0].user;
if(userId != null) {
db.query("SELECT `id`, `name` FROM `t_users` WHERE `id` = ?", [userId], (err, res2) => {
if(err) {
res.json(message_error);
} else {
//Final json structure
res.json({
id: res1[0].id,
lat: res1[0].lat,
lon: res1[0].lon,
elevation: res1[0].elevation,
rating: res1[0].rating,
rating_count: res1[0].rating_count,
user: {
id: res2[0].id,
name: res2[0].name
}
});
}
});
} else {
res.json({
id: res1[0].id,
lat: res1[0].lat,
lon: res1[0].lon,
elevation: res1[0].elevation,
rating: res1[0].rating,
rating_count: res1[0].rating_count
});
}
}
});
});
I'm hard-coding the json structure so if the userId in my table is null I don't print the user object in the json, otherwise I print it. But that's not a good idea, as I will also add more queries in the same route. Is there a simple way to create just one json at the end of all the queries and if some values are null not showing it?
Also, would it be better to use async and await functions to do it, instead of this way?
Thanks!
use util maybe better way to get result when querying data.
const mysql = require('mysql');
const util = require('util');
// ? connection pool
let db = mysql.createPool({
connectionLimit: 10,
host: 'localhost',
port: 3306,
user: 'root',
password: 'secretpass',
database: 'yourdatabasename'
});
// ? check connection
db.getConnection((err, connection) => {
if (err) {
console.log('database connection failed');
} else {
console.log('database connection resolved');
connection.release();
}
});
// ? formatter query
function queryString(spName, ...queryParam) {
return mysql.format(spName, queryParam);
}
// ? do query
let performQuery = util.promisify(db.query).bind(db)
module.exports = {
performQuery,
queryString
}
then use the function above like this :
app.get('/api/place/:id', async (req, res) => {
const id = req.params.id;
const message_error = '{"status": "failed", "message": "Unable to fetch data"}';
try {
let prepareOne = queryString(SELECT `id`, `user`, `lat`, `lon`, `elevation`, `rating`, `rating_count`, `country`, `continent`, `locality` FROM `t_points` WHERE id = ?", id);
let resultOne = await performQuery(prepareOne);
let userId = resultOne[0].user;
if (userId != null) {
let prepareTwo = queryString("SELECT `id`, `name` FROM `t_users` WHERE `id` = ?", userId);
let resultTwo = await performQuery(prepareTwo);
res.json({
...resultOne,
user: {
...resultTwo
}
});
}
} catch (err) {
res.send(message_error);
}
Related
The client.mysqllocal function is supposed to return rows.
My current code: https://pastebin.com/hgt2DwSY
const mysql = require('mysql2');
let pool = mysql.createPool({
connectionLimit : 10,
host : 'localhost',
user : 'user',
password : 'passwd',
database : 'database',
waitForConnections: true,
queueLimit: 0
});
client.mysqllocal = async function localmysql(mySQL_db, mySQL_query){
let rows;
pool.getConnection(async function(err, conn) {
if (err){
console.log(err);
}
conn.changeUser({database : mySQL_db}, function(err) {
if (err){
console.log(err);
}
});
conn.query(mySQL_query), function (error, results, fields) {
rows = results
}
conn.release()
})
return rows;
}
let func = client.mysqllocal('database','SELECT * FROM `guild_config` WHERE `guild_id` = "guildid"');
console.log(func);
P.S. this is a code snippet, I have a client defined above :) .
In this instance I would probably promisify your querying function
const mysql = require('mysql2');
let pool = mysql.createPool({
connectionLimit : 10,
host : 'localhost',
user : 'user',
password : 'passwd',
database : 'database',
waitForConnections: true,
queueLimit: 0
});
client.mysqllocal = function localmysql(mySQL_db, mySQL_query) {
return new Promise((resolve, reject) => {
let rows;
pool.getConnection(async function(err, conn) {
if (err){
reject(err);
}
conn.changeUser({database : mySQL_db}, function(err) {
if (err){
reject(err);
}
});
conn.query(mySQL_query), function (error, results, fields) {
rows = results
}
conn.release()
})
resolve(rows);
});
}
client.mysqllocal('database','SELECT * FROM `guild_config` WHERE `guild_id` = "guildid"').then(func => console.log(func));
const mysql = require('mysql2');
let pool = mysql.createPool({
connectionLimit : 10,
host : 'localhost',
user : 'user',
password : 'passwd',
database : 'database'
});
client.getLocal = (db, query, callback) => {
pool.getConnection((err, con) => {
if(err) reject(err);
con.changeUser({
database: db
}, (err) => { if(err) reject(err) });
con.query(query, (error, result, fields) => {
return callback(result);
})
})
}
client.getLocal('dbname', 'query', (result) => {
console.log(result)
})
What I did here was specify a function inside a function so that I can play with the data that the query gives me, and doesn't require async either! Promises are much more preferred but I consider my callbacks to be a bit more flexible, and beginners are prone to confusion when using promises, just like I was! I'd say use a promise as the question above me does.
This is the very basic route I am using:
router.get('/:date', async (req, res) => {
let tracks = []
let query = 'SELECT * from `tracks` where `playlistDate` = \''+req.params.date+'\''
let result = await pool.query(query)
console.log(result)
})
I know this by itself won't work, but what do I need to do to be able to use await on the query function like this?
This is the pool, with the credentials and addr changed.
var pool = mysql.createPool({
poolLimit : 10,
host : 'HOST',
user : 'USER',
password : 'PASS',
database : 'DB'
});
Maybe this logic would help. It depends on how you structured it.
var pool = mysql.createPool({
poolLimit : 10,
host : 'HOST',
user : 'USER',
password : 'PASS',
database : 'DB'
});
const getTracks = (date) => {
return new Promise((resolve, reject) => {
let query = 'SELECT * FROM tracks WHERE playlistDate = ?'
pool.query(query, [date], (err, res) => {
if (err) {
reject(err);
return;
}
resolve(res);
})
})
};
router.get('/:date', async (req, res) => {
try {
let tracks = await getTracks(req.params.date);
return res.status(200).json(tracks);
} catch (err) {
return res.status(400).json(err);
}
})
I am working on an application where I can save destinations to my Mongo DB. I would like to throw a custom error when trying to save a destination that already exsist in the DB. Mongoose prevents that from happening but I want clear and userfriendly error handling.
// post a new destination
router.post('/',
(req, res) => {
const newCity = new cityModel(
{
name: req.body.name,
country: req.body.country
}
)
newCity.save()
.then(city => {
res.send(city)
})
.catch(err => {
res.status(500).send('Server error')
})
});
Before saving a new destination, you can check if there is document already using findOne method, and if it exists you can return a custom error.
router.post("/", async (req, res) => {
const { name, country } = req.body;
try {
const existingDestination = await cityModel.findOne({name,country});
if (existingDestination) {
return res.status(400).send("Destionation already exists");
}
let newCity = new cityModel({ name, country });
newCity = await newCity.save();
res.send(city);
} catch (err) {
console.log(err);
res.status(500).send("Server error");
}
});
Note that I guessed the duplication occurs when the same country and name exist. If it is not what you want, you can change the query in findOne.
Since you've created unique index, When you try to write duplicate then the result would be :
WriteResult({
"nInserted" : 0,
"writeError" : {
"code" : 11000,
"errmsg" : "E11000 duplicate key error index: test.collection.$a.b_1 dup key: { : null }"
}
})
Your code :
Constants File :
module.exports = {
DUPLICATE_DESTINATION_MSG: 'Destionation values already exists',
DUPLICATE_DESTINATION_CODE: 4000
}
Code :
//post a new destination
const constants = require('path to constants File');
router.post('/',
(req, res) => {
const newCity = new cityModel(
{
name: req.body.name,
country: req.body.country
}
)
try {
let city = await newCity.save();
res.send(city)
} catch (error) {
if (error.code == 11000) res.status(400).send(`Destination - ${req.body.name} with country ${req.body.country} already exists in system`);
/* In case if your front end reads your error code &
it has it's own set of custom business relates messages then form a response object with code/message & send it.
if (error.code == 11000) {
let respObj = {
code: constants.DUPLICATE_DESTINATION_CODE,
message: constants.DUPLICATE_DESTINATION_MSG
}
res.status(400).send(respObj);
} */
}
res.status(500).send('Server error');
})
I am trying to use an api to get the current value of a stock and multiply by the users stock.
When I make a call the route I get empty data, and when I print the value of the callback I get an empty array
function user_cur_portfolio(port, callback) {
let portfolio = [];
port.forEach( (stock) => {
var ticker = stock.name.toLowerCase();
alpha.data.quote(`${ticker}`).then(data => {
var fixed = Number((data['Global Quote']['05. price'] * stock.shares).toFixed(2));
let curr = {
name : ticker,
shares: stock.shares,
value : fixed
}
portfolio.push(curr)
});
})
callback(portfolio)
}
router.get('/portfolio', (req, res, next) => {
if (req.session.userId !== undefined){
User.findOne({ _id : req.session.userId }).exec(function (err, user) {
if (err)
next(err);
user_cur_portfolio(user.portfolio, (port)=>{
console.log(port);
res.render('portfolio', { portfolio: port, balance: user.balance});
});
})
} else {
res.redirect('/users/login');
}
});
When I make a call the route I get empty data Because alpha.data.quote is an async function and forEach is a sync function therefore, you will not be getting data in port variable.
So the best work around to this, is to use async await with all the synchronous function to behave them like async
async function user_cur_portfolio(port) {
let portfolio = [];
await Promise.all(
port.map(async stock => {
var ticker = stock.name.toLowerCase();
const data = await alpha.data.quote(`${ticker}`);
var fixed = Number((data['Global Quote']['05. price'] * stock.shares).toFixed(2));
let curr = {
name: ticker,
shares: stock.shares,
value: fixed
};
portfolio.push(curr);
})
);
return portfolio;
}
router.get('/portfolio', (req, res, next) => {
if (req.session.userId !== undefined) {
User.findOne({ _id: req.session.userId }).exec(async function(err, user) {
if (err) next(err);
const port = await user_cur_portfolio(user.portfolio);
console.log(port);
res.render('portfolio', { portfolio: port, balance: user.balance });
});
} else {
res.redirect('/users/login');
}
});
I got a data from MySQL and push it into new Array but it when I log it. It doesn't have any data. I use for loop to get each data from DB and I don't know how to push RowDataPacket into new Array.
Or is there any way to combine two SQL into one SQL line?
router.get(`/find-users/:queryString`, function(req, res, next) {
let queryString = req.params.queryString;
db.query(
`SELECT distinct userId from project WHERE keyword like "%${queryString}%"`,
function(error, data) {
if (error) {
console.log(error);
}
// console.log(data);
let userArray = [];
for (let i = 0; i < data.length; i++) {
db.query(
`SELECT * FROM user WHERE loginId='${data[i].userId}'`,
function(error, userData) {
if (error) {
console.log(error);
} else {
console.log("-----------------------");
console.log(userData[0]);
// userArray[i] = userData;
userArray.push(userData[0]);
}
}
);
}
console.log(`-------`);
console.log(userArray);
console.log(`-------`);
}
);
});
I have to make array like this.
[ RowDataPacket {
loginId: '박동찬',
displayId: '107688875506148574770',
name: '박동찬',
bio: 'NO BIO',
RowDataPacket {
loginId: 'jaagupkymmel',
displayId: '1156051',
name: 'Jaagup Kümmel',
bio: 'NO BIO' }
]
But it only returns like this
Result
-------
[]
-------
const {promisify} = require('util')
router.get(`/find-users/:queryString`, async function (req, res, next) {
const query = promisify(db.query).bind(db);
let queryString = req.params.queryString;
const data = await query(`SELECT distinct userId from project WHERE keyword like "%${queryString}%"`)
if (!data) {
console.log("error");
}
// console.log(data);
let userArray = [];
for (let i = 0; i < data.length; i++) {
const userData = await query(`SELECT * FROM user WHERE loginId='${data[i].userId}'`)
if (!userData) {
console.log("error");
} else {
console.log("-----------------------");
console.log(userData[0]);
// userArray[i] = userData;
userArray.push(userData[0]);
}
}
console.log(`-------`);
console.log(userArray);
console.log(`-------`);
});
use this instead
function(error, userData, fields)
and you get an array like shown here https://www.w3schools.com/nodejs/nodejs_mysql_select.asp
And please read up on sql injection and node.js Preventing SQL injection in Node.js
in addition to above answer,
[{
RowDataPacket: {
loginId: '박동찬',
displayId: '107688875506148574770',
name: '박동찬',
bio: 'NO BIO',
RowDataPacket: {
loginId: 'jaagupkymmel',
displayId: '1156051',
name: 'Jaagup Kümmel',
bio: 'NO BIO' }
}
}]
the json should be key value pair other it wont work.