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." });
}
});
};
Related
I have made a login system where i am logging in using mobile number and password.Before my code was showing welcome back 03**** ** because i had used request.session.number. I have changed the code so that i can get first name from the database whether employer or helper login.But it is not working.It is showing welcome back, undefined. I am unable to get the name from database.Can anyone tell me how to get it or where i am wrong. I will be thankful if anyone can help me in this regard.
app.post('/auth', function(request, response) {
var number = request.body.number;
var password = request.body.pwd;
if (number && password) {
var sql = `SELECT fyp_helpers.Mobile_number AS number,fyp_helpers.Password AS Password FROM fyp_helpers WHERE Mobile_number = ? UNION SELECT
fyp_employers.Employer_Contact AS number , fyp_employers.Employer_Password AS Employer_Password FROM fyp_employers
WHERE Employer_Contact = ?`;
connection.query(sql, [number, number], function (error, results, fields) {
if (results.length > 0) {
var hashedPassword = results[0].Password;
bcrypt.compare(password, hashedPassword, function (cryptErr, cryptResult) {
if (cryptResult) {
request.session.loggedin = true;
request.session.number = number;
var name= connection.query(`select fyp_helpers.First_Name As name FROM fyp_helpers UNION select fyp_employers.Employer_Fname As name FROM fyp_employers`,[name,name],function(error, results, fields)
{
if(!err)
console.log(name);
else
console.log(error);
})
response.redirect('/home');
} else {
response.send('Incorrect Password!');
console.log(cryptErr);
}
response.end();
});
} else {
response.send('User not registered');
}
});
}
else {
response.send('Please enter Username and Password!');
response.end();
}
});
app.get('/home', function(request, response) {
if (request.session.loggedin) {
response.send('Welcome back, ' + request.session.name + '!');
} else {
response.send('Please login to view this page!');
}
response.end();
});
app.listen(3000);
Everything is working fine but the only problem is it is not getting name.
Edit
var name= connection.query(`select fyp_helpers.First_Name As name FROM fyp_helpers UNION select fyp_employers.Employer_Fname As name FROM fyp_employers`,[name,name],function(error, results, fields)
{
if(!error) {
request.session.name = name;
console.log(name);
}
else
console.log(error);
})
I have declared name to session but it is still not working.Do i place it on wrong position or my query is not right to select the name?
You forgot to add name in request.session.name. Variable name is fetched but not used.
What I am trying to implement is that after logged in ,system will check user's role and redirect the user accordingly.
1- Admin
2- User
role field is integer type. below is my code
router.post('/signin', function (req, res, next) {
session_store = req.session;
req.assert('Emailid', 'Please fill register Email Address').notEmpty();
req.assert('Emailid', 'Email not valid').isEmail();
req.assert('password', 'Please fill the Password').notEmpty();
var errors = req.validationErrors();
if (!errors) {
Emailid = req.sanitize('Emailid').escape().trim();
password = req.sanitize('password').escape().trim();
var query = 'select * from userdetails where Emailid=? and password=?';
var sql='select role from userdetails where Emailid=?'
db.query(query, [Emailid, password], function (err, rows) {
if (err) {
var errornya = ("Error Selecting : %s ", err.code);
console.log(err.code);
req.flash('msg_error', errornya);
res.redirect('/login-Form');
} else {
if (rows.length <= 0) {
req.flash('msg_error', "Wrong email address or password. Try again.");
res.redirect('/login-Form');
}
else {
session_store.is_login = true;
session_store.user = Emailid;
db.query(sql, Emailid, function (err, result) {
if (err) throw err
else {
if (result == 1) { // facing issue here. It is directly going to else block though the user role is 1 in the mysql table
console.log(result)
res.redirect('/Dashboard');
}
else {
res.redirect('/Audit-Record');
}
}
});
}
}
});
}
else {
res.redirect('/login-Form');
}
});
I guess I am making some mistake while comparing the result value. Can anyone of you please check and let me know where I am going wrong.
Thanks in advance!
The issue got resolved. Actually I was comparing in wrong way. we need to write it like this
else {
session_store.is_login = true;
session_store.user = Emailid;
db.query(sql, Emailid, function (err, result) {
if (err) throw err
else {
**if (result[0].role == 1)** {
console.log(result)
res.redirect('/Dashboard');
}
else {
res.redirect('/Audit-Record');
}
}
});
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...
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.
How can i pass table name as variable. Basically i want to make e function in which i will take table name as a parameter and object insert record in mysql database in using nodejs
My function will be like
exports.insertIntoDb = function(tableName,insertObj) {
connection.query('INSERT INTO administrator SET ?',insertObj, function(error, result, fields) {
if(error){
res.json({
status:false,
message:'There is some problem with query'
})
}
else{
res.json({
status : true,
data : result,
message: 'user registered successfully'
})
}
});
}
But i am wondering that how to pass table name in this query which is parameter taken from function. I am asking about syntax? I am using nodejs-mysql
Try this:
exports.insertIntoDb = function(tableName,insertObj) {
connection.query('INSERT INTO ?? SET ?', [ tableName, insertObj ], ...)
};
Documented here: https://github.com/mysqljs/mysql#preparing-queries
Inside app.js:
app.put('/updateCell', async function(req, res) {
console.log("REST: PUT /updateCell");
let orderInfo = req.body;
let cellValue = orderInfo.cell;
let CustomerName = orderInfo.CustomerName;
let ColumnName = orderInfo.columnName;
connection.query("UPDATE vehicles SET ?? = ? WHERE order_CustomerName = ?", [columnName, cellValue, customerName],
function(err, result) {
if (err) throw err;
});
res.send();
});
example:
//cellValue = "Fluffiest hat of them all";
//customerName = "Jenny Hopkins";
//columnName = "description";
So the SQL query would be the same as:
UPDATE order SET description = "fluffiest hat of them all" WHERE order_CustomerName = "Jenny Hopkins";