I am working on a proyect developing a REST API and would like some feedback on this simple user creation middleware. How would you optimize it in order to not have this much "if" statements?
This is the code:
function middlewareUserCreation(req, res, next) {
const conditionUserName = req.body.userName != null && req.body.userName != undefined;
const conditionFullName = req.body.fullName != null && req.body.fullName != undefined;
const conditionEmail = req.body.email != null && req.body.email != undefined;
const conditionTelephone = req.body.telephone != null && req.body.telephone != undefined;
const conditionAddress = req.body.address != null && req.body.address != undefined;
const conditionPassword = req.body.password != null && req.body.password != undefined;
if (conditionUserName) {
if (conditionFullName) {
if (conditionEmail) {
if (conditionTelephone) {
if (conditionAddress) {
if (conditionPassword) {
const newUserName = req.body.userName;
const newEmail = req.body.email;
const checkAdmin = req.body.isAdmin;
for (user of validUsersArray) {
if (newUserName != user.userName) {
if (newEmail != user.email) {
return next();
}
res.send("Email taken, please use another one");
}
}
res.send("Username taken, please try another one");
}
res.send("A password is required");
}
res.send("Please provide a delivery address");
}
res.send("A contact telephone is required");
}
res.send("An email is required");
}
res.send("Please provide your full name");
}
res.send("Choose an username");
};
There could be a number of smaller ideas, like using !!:
// this:
const conditionUserName = req.body.userName != null && req.body.userName != undefined;
// is the same as this:
const conditionUserName = !!req.body.userName;
Snippet to show this:
const long = (data) => {
return data != null && data != undefined
}
const short = (data) => {
return !!data
}
const arr = [
null,
undefined,
'has value',
]
arr.forEach(e => {
console.log("long:", long(e))
console.log("short:", short(e))
})
Then you could extract the rules, create a validation function based on the rules and res.send according to the validation results:
const validationMessages = {
userName: "Choose a username",
fullName: "Please provide your full name",
email: "An email is required",
telephone: "A contact telephone is required",
address: "Please provide a delivery address",
password: "A password is required",
}
const validate = (messages, data) => {
// return the message if any of the keys are null or undefined (truthy)
// return false otherwise (falsy)
const msg = Object.keys(messages).find(key => !(!!data[key]))
return msg ? messages[msg] : false
}
function middlewareUserCreation(req, res, next) {
// if invalid it will be a message; if valid it will be undefined
const isInvalid = validate(validationMessages, req.body)
if (isInvalid) {
res.send(isInvalid)
} else {
const newUserName = req.body.userName;
const newEmail = req.body.email;
const checkAdmin = req.body.isAdmin;
for (user of validUsersArray) {
if (newUserName != user.userName) {
if (newEmail != user.email) {
return next();
}
res.send("Email taken, please use another one");
}
}
res.send("Username taken, please try another one");
}
}
Related
I have this JS code that I've been looking at for the past 2 hours and cannot find the reason why the variable "message" doesn't get rewritten to "User already exists.". The thing is that "Inside first if" runs, but the variable message doesn't get rewritten to "User already exists."
async function postRegister(req, res) {
const familyName = req.body.familyName;
const email = req.body.email;
const password = req.body.password;
const repeatPassword = req.body.repeatPassword;
let message = 'msg';
// Check if user already exists
await db
.promise()
.query(
'SELECT * FROM users WHERE email = ?',
[email],
function (err, results) {
if (results.length > 1) {
console.log('Inside first if');
message = 'User already exists.';
return;
} else {
if (password !== repeatPassword) {
console.log('passwords do not match');
} else {
const newUser = new User(familyName, password, email);
newUser.save();
res.redirect('/login');
}
}
}
);
console.log(message);
res.render('authentication/register', { message: message });
}
Try this. Do not find all the users. Find only one since there will not be any duplicate entry.
try {
const user = await User.findOne({ email: email})
let message = ''
if(user) {
message = 'User already exists'
// better return the response here
}
if (password !== repeatPassword) {
message 'passwords do not match'
}
if(message) {
// Some error message is present, perform your action here
}
// Create your user
res.redirect('/login')
} catch(err) {
console.log(err)
}
I used the bcrypt, cookie-parser, jsonwebtoken, validator... librarys.
And I am using express, jwt, and sequelize.
The situation I am in is that when I sign up as a member, it is first added to the DB.
If you create an account again, it will be duplicated.
const bcrypt = require("bcrypt");
const { User } = require("../../models");
const jwt = require("jsonwebtoken");
const {
registerValidator,
loginValidator,
} = require("../../validation/authValidation");
require("dotenv").config();
exports.register = async (req, res) => {
const { errors, isValid } = registerValidator(req.body);
let { email, username, password } = req.body;
if (!isValid) {
return res.status(400).json(errors);
}
const emailExists = await User.findOne({ Where: { email: email } });
console.log(emailExists);
if (emailExists) {
return res.status(400).json({ message: "이미 사용중인 이메일입니다." });
}
const salt = await bcrypt.genSalt(10);
const hashedPassword = await bcrypt.hash(password, salt);
const newUser = {
username: username,
email: email,
password: hashedPassword,
};
User.create(newUser)
.then((save) => {
res.status(200).json({ status: "Success", new_user_id: save.id });
})
.catch((err) => res.status(500).json({ message: err + "잘안됩니다." }));
As in the postman image, the token was initially stored in a cookie and when the DB was checked, it was confirmed that a record was added.
The validation was written as follows through the validator library.
const Validator = require("validator");
const isEmpty = (value) =>
value === undefined ||
value === null ||
(typeof value === "object" && Object.keys(value).length === 0) ||
(typeof value === "string" && value.trim().length === 0);
const registerValidator = (data) => {
let errors = {};
data.username = !isEmpty(data.username) ? data.username : "";
data.email = !isEmpty(data.email) ? data.email : "";
data.password = !isEmpty(data.password) ? data.password : "";
if (Validator.isEmpty(data.username)) {
errors.username = "유저네임을 입력해주세요.";
}
if (Validator.isEmpty(data.email)) {
errors.email = "이메일을 입력해주세요.";
}
if (Validator.isEmpty(data.password)) {
errors.password = "비밀번호를 입력해주세요.";
}
if (!Validator.isLength(data.password, { min: 6, max: 30 })) {
errors.password = "비밀번호는 6자 이상 30자 미만으로 작성해야합니다.";
}
return {
errors,
isValid: isEmpty(errors),
};
};
const loginValidator = (data) => {
let errors = {};
data.email = !isEmpty(data.email) ? data.email : "";
data.password = !isEmpty(data.password) ? data.password : "";
if (!Validator.isEmail(data.email)) {
errors.email = "Email is invalid";
}
if (Validator.isEmpty(data.email)) {
errors.email = "Email is required";
}
if (Validator.isEmpty(data.password)) {
errors.password = "Password is required";
}
return {
errors,
isValid: isEmpty(errors),
};
};
module.exports.registerValidator = registerValidator;
module.exports.loginValidator = loginValidator;
How do I fix it? Help.
As I understood your question you want to put validation for uniqueness of email in database. If so you can add some code in your Users model:
// inside attributes definition of your model
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true // throws validation error if creating instance is already stored
} ...
So in your code you can omit parts of finding User and checking whether emails match with requested one. You just write
await User.create(newUser) // if uniqueness validation fails it will throw error and no dublication would be in database
I am trying to create a log in function, so that when I input the username and password that is in the database, it will check to see if this matches and return a newsfeed. The database is like this:
var database=[
{
username:"Jay",
password:"1234",
},
{
username:"Kate",
password:"4567",
},
{
username:"Betty",
password:"789",
}
]
var newsfeed=[
{
username:"Jay",
timeline:"happy"
},
{
username:"Kate",
timeline:"sad"
},
{
username:"Mary",
timeline:"boring"
},
{
username:"Betty",
timeline:"peaceful",
}
];
Now I try to get the prompt in the username and password, such as "Betty" and "789", it returns "sorry, wrong username and password" but it is in the database!! Here is the code:
var usernamePrompt = prompt("what is your username");
var passwordPrompt = prompt("what is your password?");
function isUserValid(username, password) {
for (var i = 0; i < database.length; i++) {
if (database[i].username === username &&
database[i].password === password) {
return true;
} else {
return false;
}
}
}
function signIn(username, password){
if (isUserValid(username, password)){
console.log(newsfeed);
}else{
alert("sorry, wrong username and password");
}
}
signIn(usernamePrompt, passwordPrompt);
I just get the correct answer that is as below, but 1) I do not understand the difference between putting an "else" and not putting it. 2) I do not understand why we do not need to put "if (isUserValid(username, password===true))" to indicate the condition needs to be matched.
var usernamePrompt = prompt("what is your username");
var passwordPrompt = prompt("what is your password?");
function isUserValid(username, password){
for(var i=0;i<database.length;i++){
if(database[i].username===username&&
database[i].password===password){
return true;
}
}
return false;
}
function signIn(username, password){
if (isUserValid(username, password)){
console.log(newsfeed);
}else{
alert("sorry, wrong username and password");
}
}
signIn(usernamePrompt, passwordPrompt);
Thank you for your kind assistance.
In your code it will return as soon as the first object is iterated.Use some which will check if any object is matching the value that is supplied through the prompt. If any value matches then it will return true.Also use toLowerCase to make a case insensitive search
var database = [{
username: "Jay",
password: "1234"
},
{
username: "Kate",
password: "4567"
},
{
username: "Betty",
password: "789"
}
];
var newsfeed = [{
username: "Jay",
timeline: "happy"
},
{
username: "Kate",
timeline: "sad"
},
{
username: "Mary",
timeline: "boring"
},
{
username: "Betty",
timeline: "peaceful",
}
];
function isUserValid(username, password) {
return database.some(item => item.username.toLowerCase() === username.toLowerCase() && item.password.toLowerCase() === password.toLowerCase());
}
function signIn(username, password) {
if (isUserValid(username, password)) {
console.log(newsfeed);
} else {
alert("sorry, wrong username and password");
}
}
var usernamePrompt = prompt("what is your username");
var passwordPrompt = prompt("what is your password?");
signIn(usernamePrompt, passwordPrompt);
The else is returning false after the first element only .it should check for all the elements and then return false .
You can simplify using .some function.
database = [
{ username: "user1", password: "pass1" },
{ username: "user2", password: "pass2" },
{ username: "user3", password: "pass3" },
];
const isUserValid = (username, password) =>
database.some(
({ username: du, password: dp }) => du == username && dp == password
);
function signIn(username, password) {
if (isUserValid(username, password)) {
return "AUTHORIZED";
} else {
return "sorry, wrong username and password";
}
}
console.log(signIn("user", "pass"));
console.log(signIn("user2", "pass2"));
console.log(signIn("user1", "pass"));
It fails because if the username/password is not the very first one in the database then it won't match, returns false and exits the function. The for-loop does not continue looking for other matches. You only want to return false if you've checked all of the possibilities.
Separate your functions more clearly so they're easier to understand like this:
const usernamePrompt = prompt("what is your username");
const passwordPrompt = prompt("what is your password?");
function isUserValid(username, password) {
for (let i = 0; i < database.length; i++) {
if (database[i].username === username) {
// found the user. return true if the password is right, otherwise false
return database[i].password === password;
}
}
return false; // <-- false if user is never recognized
}
function signIn(username, password) {
if (isUserValid(username, password)) {
console.log(newsfeed);
} else {
alert("sorry, wrong username and password");
}
}
signIn(usernamePrompt, passwordPrompt);
So, when I first open the connection with the database, all is working fine, but when I close it and try to re-open it, the db object gives no error but returns undefined ...
Here's how I open the connection :
let conn = null;
let db = null;
async function validateLoginForm(payload, res) {
const errors = {};
let isFormValid = true;
let message = '';
if (!payload || typeof payload.email !== 'string' || payload.email.trim().length === 0) {
if (payload.email !== 'anonymous') {
isFormValid = false;
errors.email = 'Please provide your email address.';
}
}
if (!payload || typeof payload.password !== 'string' || payload.password.trim().length === 0) {
if (payload.email !== 'anonymous') {
isFormValid = false;
errors.password = 'Please provide your password.';
}
}
let stringConnection = payload.email === 'anonymous' ? 'mongodb://ds133221.mlab.com:33221/sandbox-te' : 'mongodb://' + payload.email + ':' +
payload.password + '#ds133221.mlab.com:33221/sandbox-te';
conn = await MongoClient.connect(stringConnection, await function(err, dbase) {
if (err)
{
isFormValid = false;
let errorMessage = 'Error connecting to DB';
return res.status(400).json({
success: false,
message: errorMessage,
errors: errors
});
}
else
{
db = dbase;
if (payload.email !== 'anonymous')
{
let roles = dbase.command({usersInfo: {user: payload.email, db: 'sandbox-te'}, showCredentials: true}, (err, result) => {
if (result.users[0] && result.users[0].roles[0] &&
(result.users[0].roles[0].role === 'dbOwner' || result.users[0].roles[0].role === 'readWrite'))
{
dbase.close();
return res.status(200).json({
success: true,
hasWriteRole: true
})
}
});
}
else
{
return res.status(200).json({
success: true,
hasWriteRole: false
})
}
}
});
}
The first part of the file validates a login form and the second part uses the email and password to try to open a connection with the database.
The whole function just works fine, but when I try to re-open it in the same file but another function, it won't work :
router.post('/search', (req, res) => {
db.open((err, dbase) => {
let test = dbase.collection('test');
console.log(test);
let promiseOfFind = test.find({}).toArray((err, docs) => {
console.log(docs); // RETURNS UNDEFINED ONLY IF DB WAS CLOSED EARLIER
})
});
});
If I don't close the database in the validateLoginForm function, I can retrieve documents without having to open it again, but I just want to achieve this..
What is wrong with my code ? I'm pretty new to Javascript and the API reference of the official MongoDB driver for node.js doesn't help much..
I'm using latest versions of React, Express, MongoDB official Driver for Node.JS, and of course, Node.JS
Thanks in advance !
With MongoDB you should open a single connection and re-use it through your application. You don't need the promises you've got then.
So on startup:
const connection = MongoClient.connect(stringConnection, function(err, dbase) {
// Start web server
});
I wanna return the MySQL query in Node.js, but I got some problems.
Prob1. 'var userInfo' cannot get the value from function 'Authenticate()'
Prob2. The throw will be catched by 'dbclient.query', not my code you can see.
Hope guys can help me.
app.post('/create_member_check', function(req, res) {
var Authenticate = function () {
SearchUser(req.body.email, function (isExist) {
if (isExist === true)
throw 101;
else if (req.body.email === undefined || req.body.email == "")
throw 102;
else if (req.body.password === undefined || req.body.password == "")
throw 103;
else if (isExist === undefined)
throw 104;
var user = {
"email": req.body.email,
"password": req.body.password
};
AddUser(user);
// This line cannot return the 'user' for 'Authenticate()' caller.
return user;
});
}
try {
var userInfo = Authenticate();
}
catch (err) {
var userInfo;
if (err == 101)
userInfo = "[Error] This account already exists.";
else if (err == 102)
userInfo = "[Error] Please key in 'email'.";
else if (err == 103)
userInfo = "[Error] Please key in 'password'.";
else if (err == 104)
userInfo = "[Fatal Error] SearchUser return 'undefined'.";
}
res.render("login_system/create_member_check", {
layout: false,
pagename: "create",
authenticate: userInfo
});
});
SearchUser = function (email, callback) {
dbclient.query("SELECT * FROM user WHERE email = \"" + email + "\"", function (err, results) {
if (err || results.length <= 0)
callback(false);
else
callback(true);
});
}
Authenticate method can't be synchronous. You should make asynchronous method. Try this.
app.post('/create_member_check', function(req, res) {
var Authenticate = function (req, callback) {
SearchUser(req.body.email, function (isExist) {
if (isExist === true)
return callback(101);
else if (req.body.email === undefined || req.body.email == "")
return callback(102);
else if (req.body.password === undefined || req.body.password == "")
return callback(103);
else if (isExist === undefined)
return callback(104);
var user = {
"email": req.body.email,
"password": req.body.password
};
AddUser(user); //this is maybe asynchronous, again
callback(null, user);
});
}
Authenticate(req, function(err, user){
var userInfo;
if (err == 101)
userInfo = "[Error] This account already exists.";
else if (err == 102)
userInfo = "[Error] Please key in 'email'.";
else if (err == 103)
userInfo = "[Error] Please key in 'password'.";
else if (err == 104)
userInfo = "[Fatal Error] SearchUser return 'undefined'.";
res.render("login_system/create_member_check", {
layout: false,
pagename: "create",
authenticate: userInfo
});
});
});
And read this article ;)