javascriptnode.jsmongodbpure-function

How can I validate this in more elegant way?


Im trying to do login/register module in my project. This is my login function. I would like to have one function that will validate all things for me so I dont have to use so many "if" statements. I was trying to do with pure function but completely don't know how to do it. Can someone help me ?

const loginUser = async (req, res, next) => {
  const { password, email } = req.body;

  if (!email) {
    return res.status(400).json({
      message: "Error: Email cannot be blank.",
    });
  }
  if (!password) {
    return res.status(400).json({
      message: "Error: Password cannot be blank.",
    });
  }

  try {
    const user = await User.findOne({ email: email });

    if (!user)
      return res.status(400).json({
        message: "Invalid user",
      });

    if (!validPassword(password, user.password))
      return res.status(400).json({
        message: "Invalid password",
      });

    const { name, likedArr, _id } = user;
    const token = crypto.randomBytes(32).toString("hex");
    const userSession = new UserSession({ userId: _id, token });
    await userSession.save();
    return res.status(200).json({
      message: "Valid login",
      token: token,
      user: {
        name,
        likedArr,
        userId: _id,
      },
    });
  } catch (err) {
    next(err);
  }
};


Solution

  • Abstracting my comments into an answer.

    On Pure Functions:

    If I understand pure functions correctly, I don't think you can have a pure function that calls an external API which may fail, since the same inputs may possibly return different results depending on the external state of the API (unless that API is guaranteed pure itself somehow). (Definition of a pure function)

    On Repetition:

    I genuinely think you don't have a lot of repetition here. Your code is clear and only has 4 conditionals, all for things you need to test for. You could abstract the similarities of your JSON returns into something like a template string depending on the conditions, but I think that could add clutter and opacity to your code, which isn't a good trade-off if you do it too much.

    If you want an example of what I mean here:

    if (!email) {
      return res.status(400).json({
        message: "Error: Email cannot be blank.",
      });
    }
    if (!password) {
      return res.status(400).json({
        message: "Error: Password cannot be blank.",
      });
    }
    

    Can become...

    if (!email || !password) {
      return res.status(400).json({
        message: `Error: ${!email ? 'Email' : 'Password'} cannot be blank.`,
      });
    }