0

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 } );
              }  
        });

3 Answers 3

1

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.

Sign up to request clarification or add additional context in comments.

Comments

0

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.

Comments

0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.