Update only some attributes of user in MySQL using Nodejs - mysql

I have a put route which can be used to update the user. Everything works fine unless the user will only provide only some params instead of all. How I can fix this? Are there some "simple" solutions for this problem? Because if the user only update his email everything else will be inserted empty..
const id: number = req.params.id;
const password: string = req.body.password;
const email: string = req.body.email;
const lastname: string = req.body.lastname;
const firstname: string = req.body.firstname;
const phoneNumber: string = req.body.phoneNumber;
const permissionID: number = req.body.permissionID;
const imageUrl: string = String(imagePath);
const passwordHash = bcrypt.hashSync(password, 10);
const insertData: [string, string, string, string, string, string, number, number] = [email, passwordHash, phoneNumber, firstname, lastname, imageUrl, permissionID, id];
const query = `UPDATE Users SET email = ?, password = ?, phone_number = ?, first_name = ?, last_name = ?, image_url = ?, permission_id = ? WHERE user_id = ?;`;
connection.query(query, insertData, (err: MysqlError | null) => {
if (!err) {
res.status(200);
res.json( { "Message": "Successfull user was updated" } );
} else {
res.status(500);
res.json( { "Database Error ": err.message } );
}
});

Okay I wrote something I hope this post will help someone. First of course it's possible to save the complete user data model in the client and to resend the complete data to the server. But why should I do this? I don't think this is effecient. If the user just want to change his lastname why I should send the whole payload...Anyway this is the way I solve it.
First I define my possible data I will receive if the user will update some attributes.
enum Validate {
password = 'password',
email = 'email',
firstname = 'first_name',
lastname = 'last_name',
phoneNumber = 'phone_number',
permissionID = 'permission_id'
}
So my function will check the received params and will return the insertData and query. As I'm using password hashing it will check as well if the user wants to update his password.
function updateParams(body: {}, options: [Validate], callBack: (insertData: string[], query: string) => void) {
const insertData: string[] = [];
let query = "";
for (const index in options) {
if (!(body[`${options[index]}`] === '' || body[`${options[index]}`] === undefined || body[`${options[index]}`] === null)) {
query += `${options[index]} = ?, `;
// If user will update password hash it
`${options[index]}` === 'password' ? insertData.push(bcrypt.hashSync(body[`${options[index]}`], 10)) : insertData.push(body[`${options[index]}`]);
}
}
callBack(insertData, query.slice(0, -2));
}
For the next step I'm using promises because there are some if/else statements. The user has the possibilities to just update his picture for example.
const updateUser = (req, res, insertData, query) => {
const promise = new Promise((resolve, reject) => {
let endQuery = '';
if (req.file) {
image.uploadImageToStorage(req.file)
.then((imagePath) => {
if (Object.keys(req.body).length === 0) {
endQuery = `UPDATE Users SET image_url = ? WHERE user_id = ?;`;
insertData.push(String(imagePath));
insertData.push(req.params.id);
resolve([endQuery, insertData]);
} else {
endQuery = `UPDATE Users SET ${query}, image_url = ? WHERE user_id = ?;`;
insertData.push(String(imagePath));
insertData.push(req.params.id);
resolve([endQuery, insertData]);
}
}).catch((error) => {
reject(error.message );
});
} else {
endQuery = `UPDATE Users SET ${query} WHERE user_id = ?;`;
insertData.push(req.params.id);
resolve([endQuery, insertData]);
}
});
return promise;
};
Now I can just use my route.
app.put('/api/v1/users/:id', image.multerMiddleware.single('image'), (req, res) => {
if (((Object.keys(req.body).length !== 0) || req.file) && !isNaN(req.params.id)) {
updateParams(req.body, [Validate.password, Validate.email, Validate.lastname, Validate.firstname, Validate.phoneNumber, Validate.permissionID], (insertData, query) => {
updateUser(req, res, insertData, query)
.then((result) => {
connection.query(result[0], result[1], (err: MysqlError | null) => {
if (!err) {
res.status(200);
res.json({ "Message": "Successfull user was updated" });
} else {
res.status(500);
res.json({ "Database Error ": err.message });
}
});
}).catch((error) => {
res.status(500);
res.json({ "Error ": error.message });
});
});
} else {
res.status(400);
res.json({ "Error": "Please provide the correct paramaters" });
}
});
So now
The user can update only some params
The user can update some params and his picture
The user can update only his picture
It work's fine now.

What I do for when someone is editing a user (or other type of data) is that I retrieve the entire data for the user and show it on the editing form. Then when they make the updates, I send all the data up. This way when I do the SQL update, it will re-save the unchanged data as well as the changed data.
Your other option is a series of conditionals which add to the update statement based off what fields are sent in to update.

You either set only those values that were provided, or, if you really insist on updating all columns (why not the PK while you're at it) you qould query them first.

Related

Update only one column in user info - MySQL NodeJS

I'm builing a classic CRUD (create, read, update, delete) API with NodeJS/Express and MySQL.
I created a route to update my user informations that works fine.
The problem :
If I dont send EVERY data (first_name, last_name etc...), the columns with no data update to undefined in MySQL database, and the column with data don't update. I would like that if I don't send data, no change happens for the columns, and only the one with datas change.
Here is my controller :
module.exports.updateUser = (req, res, next) => {
if (req.method == "PUT") {
let userDetails = `UPDATE users SET first_name = '${req.body.first_name}', last_name = '${req.body.last_name}', user_name = '${req.body.user_name}' WHERE id = ${req.params.id}`;
sql.query(userDetails, function (err, result) {
if (!err) {
res.status(200).json({ message: "User infos updated." })
} else {
res.status(401).json({ message: "Error when updating user infos." })
}
})
}
}
So, if I make a PUT request on an existing user in db with only the mail for example :
{
"mail": "test2#test2.com"
}
all my user datas become null and user.mail stays the same.
Anyone could help me on this ?
Thank you 🙂
Use this query for update one or more filled update at a time
module.exports.updateUser = (req, res, next) => {
if (req.method == "PUT") {
let query = '';
if(req.body.first_name){
query = `first_name =
'${req.body.first_name}'`
}else
if(req.body.last_name){
query = ` last_name =
'${req.body.last_name}'`
}else
if(req.body.user_name){
query = `user_name =
'${req.body.user_name}'`
}eles if(req.body.first_name
&& req.body.last_name)
{
query =`first_name = '${req.body.first_name}', last_name = '${req.body.last_name}'`
}else if(
req.body.last_name && req.body.user_name
{
query = `last_name = '${req.body.last_name}', user_name = '${req.body.user_name}'`
}else if(req.body.first_name
&& req.body.last_name && req.body.user_name)
{
query =`first_name = '${req.body.first_name}', last_name = '${req.body.last_name, user_name = '${req.body.user_name}'}'`
}
let userDetails = `UPDATE users SET ${query} WHERE id = ${req.params.id}`;
sql.query(userDetails, function (err, result) {
if (!err) {
res.status(200).json({ message: "User infos updated." })
} else {
res.status(401).json({ message: "Error when updating user infos." })
}
})
}
}
Thanks to #jutendra, it works.
After few months (and gained skills), here is a cleaner version if ever someone is interested :
module.exports.updateUser = (req, res, next) => {
if (req.method !== "PUT") return;
const updates = [];
if (req.body.first_name) updates.push(`first_name = '${req.body.first_name}'`);
if (req.body.last_name) updates.push(`last_name = '${req.body.last_name}'`);
if (req.body.user_name) updates.push(`user_name = '${req.body.user_name}'`);
if (updates.length === 0) {
res.status(400).json({ message: "No updates provided" });
return;
}
const query = `UPDATE users SET ${updates.join(", ")} WHERE id = ${req.params.id}`;
sql.query(query, (err, result) => {
if (!err) {
res.status(200).json({ message: "User infos updated." });
} else {
res.status(401).json({ message: "Error when updating user infos." });
}
});
};

Foreach loop in nodejs

My Project uses Node JS + My SQL
I am running a simple query that checks whether entries exist in database for some params.
If they exist, it then updates them, else it inserts them.
The problem is, it is entering the last entry n times instead of each being unique.
When updating also, it is not able to identify the right row.
router.post('/feeRegister', asyncMiddleware( async(req, res) => {
let post= {
...........
}
JSON.parse(req.body.fees).forEach((i,index) => {
let sql = `SELECT id, period, amount FROM feeregister WHERE schoolId = '${req.body.schoolId}' AND studentId = '${req.body.student}' AND classes = '${req.body.classes}' AND year = '${req.body.year}' AND type = '${JSON.parse(req.body.fees)[index][1]}';`
pool.query(sql, async(err, results) => {
try{
if(err){ throw err }
if(results){
if(results.length){
console.log('Entry Exists', index, results[0].id)
let post2= {
...............
}
let sql2 = `UPDATE feeregister SET ? WHERE id = '${results[0].id}'`;
pool.query(sql2, post2, async(err, results) => {
try{
if(err){ throw err }
if(results){}
}catch(e){ func.logError(e); res.status(500); return; }
})
}else{
console.log('Entry does not exist', index)
let sql = `INSERT INTO feeregister SET ?`
pool.query(sql, post, async(err, results) => {
try{
if(err){ throw err }
if(results){}
}catch(e){ func.logError(e); res.status(500); return; }
})
}
}
}catch(e){ func.logError(e); res.status(500); return; }
})
});
}))
Iam connecting to database like :
var mysql = require('mysql')
const pool = mysql.createPool({
host: 'localhost',
user: 'root',
database: 'dBName',
password: '',
multipleStatements: true
});
module.exports = pool;
Perhaps you should call the query function with async-await method.
router.post('/feeRegister', asyncMiddleware( async(req, res) => {
let post= {
//
}
JSON.parse(req.body.fees).forEach(async (i,index) => {
let sql = `SELECT id, period, amount FROM feeregister WHERE schoolId = '${req.body.schoolId}' AND studentId = '${req.body.student}' AND classes = '${req.body.classes}' AND year = '${req.body.year}' AND type = '${JSON.parse(req.body.fees)[index][1]}';`
const promisePool = pool.promise();
let results = await promisePool.query(sql);
if(results){
if(results.length){
console.log('Entry Exists', index, results[0].id)
let post2= {
//
}
let sql2 = `UPDATE feeregister SET ? WHERE id = '${results[0].id}'`;
await promisePool.query(sql2);
}else{
console.log('Entry does not exist', index)
let sql = `INSERT INTO feeregister SET ?`
await promisePool.query(sql);
}
}
});}));

Strange nodejs behaviour when logging in a user

The problem is that it shows that it is successfully logged in (201) without the redirect code, but with it, it shows a 302 error and the email_address is undefined.
What could be the problem here? I still can't come to a conclusion.
The problem may be in the order of the code I guess?
const login = async (req, res, next) => {
const { email_address, password, user_email, user_password}: { email_address: string, password: string, user_email: string, user_password: string } = req.body;
try {
const userWithDetails = 'SELECT * FROM users WHERE email_address = user_email AND password = user_password'; //w form info
if (userWithDetails) {
req.session.loggedin = true; //true
req.session.email_address = email_address; //undefined
console.log(req.session.email_address)
// return res.redirect('./index.html')
}
res.status(201).send('Succesfully signed in');
// res.status(403).send('Password is not correct');
} catch(error) {
res.status(404).send(`User with email ${email_address} not found!`);
}
await next;
};
NEW CODE ***
const login = async (req, res, next) => {
const { email_address, password}: { email_address: string, password: string} = req.body;
const userWithDetails = 'SELECT * FROM users WHERE email_address = ?';
return con.query(userWithDetails, email_address, (err, results) => {
if (err) {
console.error(err);
}
const user = results.find(emailObj => emailObj.email_address === email_address);
if (results && results.length && user.email_address) {
req.session.loggedin = true;
req.session.email_address = email_address;
const matchPassword: boolean = bcrypt.compareSync(password, user.password);
if (matchPassword) {
const token = jwt.sign({ user }, 'aaaa', { expiresIn: '1h'});
res.status(200).send({message: 'Logged in', token: token});
} else {
res.status(403).send('Password is not correct');
}
} else {
res.status(404).send(`User with email ${email_address} not found!`);
}
});
await next;
}
You don't execute your sql query at any point.
You just say :
query = 'select blabla'
if(query){...}
Of course this will always be true. You want to run the query on your database.
Also in your query you don't properly use the variables, see string formatting :
let my_var = `SELECT xxx from xxx where username = '${username}'`
Also please sanitize the parameters to prevent SQL Injection...

Update mysql with nodejs

I have a list of movies here I am trying to change the one with id
I am trying to change it to this
Below is my code
app.put('/movielist/updateMovie',(req,res) =>{
let update = req.body;
mysqlConnection.query("UPDATE movielist SET name = ?,thumnail_path = ?, description = ?, year_released = ?, language_released = ? WHERE idmovielist = ?",
[update.name,update.thumnail_path,update.description,update.year_released,update.language_released,update.idmovielist],
(err,result) => {
if (!err) {
res.send("Movie list is updated");
} else {
console.log(err);
}
});
});
You are probably looking for something like this :
app.put('/movielist/updateMovie/:id',(req,res) =>{
var id = req.params.id
var update = req.body
mysqlConnection.query('UPDATE movielist SET ? WHERE idmovielist = ?', [update, id], function (err, results) {
if (!err) {
res.send("Movie list is updated");
} else {
console.log(err);
}
})
})
In this case you have from your frontend part to pass the id of the movie you want to update and get it in the params.
You could also pass it in the body of your request without adding it to the url

Extend variables outside MySQL query function in NodeJS

I tried to run a function which returns a value back but am getting undefined.
function getMessageId(myId, user){
$query = "SELECT * FROM startMessage WHERE (userFrom = '"+myId+"' AND userTo = '"+user+"') OR (userFrom = '"+user+"' AND userTo = '"+ myId+"')";
connect.query($query, function(error, rows){
sql = rows[0];
console.log(sql);
return sql.id;
})
}
// running the function
msgId = getMessageId(userFrom, userTo);
console.log(msgId);
Now when I tried to console.log the sql I get the expected result like
{
id : 3,
userFrom : 3,
userTo : 1,
type : "normal",
date : "2017-06-25 06:56:34",
deleted : 0
}
But when I console.log the msgId I get undefined. I am doing this on NodeJS, please any better solution?
Short answer, Because its an asynchronous operation.
The outer console.log happens before the getMessageId returns.
If using callbacks, You can rewrite getMessageId as
let msgId
function getMessageId(myId, user, callback){
$query = "SELECT * FROM startMessage WHERE (userFrom = '"+myId+"' AND userTo = '"+user+"') OR (userFrom = '"+user+"' AND userTo = '"+ myId+"')";
return connect.query($query, function(error, rows){
sql = rows[0];
console.log(sql);
callback(sql.id);
})
}
function setMsgId(id) {
msgId = id;
}
And then call it as,
getMessageId(userFrom, userTo, setMsgId);
Further I would suggest you look into Promises.
Which would very well streamline the flow.
Using Promises, getMessageId should look something like
function getMessageId(myId, user){
$query = "SELECT * FROM startMessage WHERE (userFrom = '"+myId+"' AND
userTo = '"+user+"') OR (userFrom = '"+user+"' AND userTo = '"+
myId+"')";
const promise = new Promise((resolve, reject) => {
connect.query($query, function(error, rows){
sql = rows[0];
console.log(sql);
resolve(sql.id);
})
return promise.
}
Post this, You can use it as
getMessageId(myId, user).then((msgId) => console.log(msgId))
create a wrapper for mysql use
// service/database/mysql.js
const mysql = require('mysql');
const pool = mysql.createPool({
host : 'host',
user : 'user',
password : 'pass',
database : 'dbname'
});
const query = (sql) => {
return new Promise((resolve, reject) => {
pool.query(sql, function(error, results, fields) {
if (error) {
console.error(error.sqlMessage);
return reject(new Error(error));
}
resolve(results);
});
});
}
module.exports = { query };
then call from another script with async funcion and await
// another file, with express route example
const db = require('/service/database/mysql.js')
module.exports = async (req, res) => { // <-- using async!
let output = []; // <-- your outside variable
const sql = 'SELECT * FROM companies LIMIT 10';
await db.query(sql) // <-- using await!
.then(function(result) {
output = result; // <-- push or set outside variable value
})
.catch(e => {
console.log(e);
});
// await db.query("next query" ...
// await db.query("next query" ...
// await db.query("next query" ...
res.json(output);
}
This is probably NOT a proper way, and a hack, but sharing for information purpose (you may not like to use this)
Simply use an if else and call the function once inside the query (if true), and call it outside (if false)
if (id != null) {
// Get Details
const query = pool.execute('SELECT * FROM `sometable` WHERE `id` = ?', [id], function(err, row) {
if (err) {
console.log(err)
return
} else {
if (row && row.length) {
// Do Something
}
}
})
} else {
// Do Something
}