Node.js check if user already exists - javascript

Hello guys I am trying to check if a user already exists in the database, I have managed to stop creating a user if one already exists with the same name, however I do not seem to get the error message displayed. I am not too sure why my error is not being handled correctly. Here is my code:
// Register User
router.post('/register', function(req, res){
var name = req.body.name;
var email = req.body.email;
var username = req.body.username;
var password = req.body.password;
var password2 = req.body.password2;
//Validation
req.checkBody('name', 'Name is required').notEmpty();
req.checkBody('email', 'Email is required').notEmpty();
req.checkBody('email', 'Email is not valid').isEmail();
req.checkBody('username', 'Username is required').notEmpty();
req.checkBody('password', 'Password is required').notEmpty();
req.checkBody('password2', 'Passwords do not match').equals(req.body.password);
var errors = req.validationErrors();
if(errors){
res.render('register',{
errors:errors
});
}else{var newUser = new User({
name: name,
email:email,
username: username,
password: password
});
User.getUserByUsername(username, function(err, user){
if(err) throw err;
if(user){
return new Error('User already exists!');
}else{
User.createUser(newUser, function(err, user){
if(err) throw err;
console.log(user);
});
}
});
req.flash('success_msg', 'You are registered and can now login');
res.redirect('/users/login');
}
});

This will work exactly as you wish.I had the same issue trying to make it work and I assure you it does. Cheers!
User.getUserByUsername(username, function(err, user){ //must check if user exists
if(err) throw err;
if(user){
console.log('existing...'); //may be deleted
req.flash('error_msg', 'This username already exists');
res.redirect('/users/register');
} else {
User.createUser(newUser, function(err, user){
if(err) throw err;
console.log(user);//may be deleted
});
req.flash('success_msg', 'You registered successfuly and you can now login');
res.redirect('/users/login');
}
})

Two things I see wrong:
You're doing synchronous operations after passing off the control to an asynchronous callback
You're not doing anything useful with the errors inside your asynchronous callback
Here the asynchronous callback is the function you pass to User.getUserByUsername
router.post('/register', function(req, res) {
...
User.getUserByUsername(username, function(err, user) {
// Any errors get thrown here need to be dealt with here
// Also you can't simply "throw" or "return" errors from this,
// You need to use Express's "next" method to handle these errors
})
// And since you've gone asynchronous above (User.getUserByUsername)
// you can't do the following here:
req.flash('success_msg', 'You are registered and can now login')
res.redirect('/users/login')
// otherwise these will ALWAYS get executed,
// regardless of what the result of `User.getUserByUsername` may be
})
You need to do something like this:
router.post('/register', function(req, res, next) { // use `next` to handle errors
...
User.getUserByUsername(username, function(err, user) {
if (err) {
next(err)
} else if (user) {
next(new Error('User already exists!'))
} else {
req.flash('success_msg', 'You are registered and can now login')
res.redirect('/users/login')
}
})
})
PS: Also you're not saving newUser (at least not in the code you've posted)

If you only want to check if user with given username exist or not, then I have used this simple trick.
What I am doing is while registering, if some error occurs then I am checking if this error is UserExistError(which is related to mongodb, and you can see it when it occurs in terminal)
If err is equal to UserExistError then I am rendering an alert which is in separate file.
register(new User({username:req.body.username}),req.body.password,function(err,user){
if(err)
{ var chk=1;
console.log(err);
if(err=='UserExistsError');
console.log("error is here");
return res.render("register",{chk:chk});
}
passport.authenticate("local")(req,res,function(){
res.redirect("/dashboard");
})
});

var candidateName = username;
var queryforUsername = {};
queryforUsername['username'] = candidateName;
User.find(queryforUsername, function(err, item){
if(err) throw (err);
if(item.length > 0){
req.flash('error_msg', 'User with username '+username+' already exists!');
res.redirect('register');
}else if(item.length == 0){
User.createUser(newUser, function(err, user){
if(err) throw err;
console.log(user);
req.flash('success_msg', 'You are registered and can now login');
res.redirect('/users/login');
});
}
});

const { usuario } = req.body;
let user = await User.findOne({ usuario});
if (user) {
return res.status(400).json({ ok: false, msg: 'El usuario ya existe' });
}
//crear nuevo user
user = new User(req.body);

Related

The right way to connect to query database mongodb multiples times

im building a website in my free time using nodejs/Handlebars.js/mongodb(mongoose),
i didnt study any web developement lessons, and i dont know the right way to do things efficiently and securely.
so in this project, i stumbled upon a problem where i had to query in the database if email already exists or no if it does, it queries again if the username already exists or not, if it does exists the user can be registred to the databse. yes it gets the job done but im not satisfyied with this approach, it seems to be unprofessional and not secure.
so can you please tell me the right way to do it ?
this is the part where i think i ve done it the wrong way
//check for errors in Req.validation and push them to errors Array
if(valErrors){
for (var i = 0; i < valErrors.length; i++) {
errors.push(valErrors[i])
}
}
//check if the username submitted exists in the database
User.findOne({'username':username}, function (err, user) {
if(user)
{
errors.push({msg:"username is already in use!"})
res.render('user/register',{
errors:errors
});
}
//if the username is not in use already check if the email is in
//use
else {
User.findOne({'email':email}, function (err, user) {
if(user){
errors.push({msg:'email is already in use !'})
res.render('user/register',{
errors:errors
});
} //if the email doesnt exists too then register this //user
else{
var coins = new Coins()
var newUser = new User({
name: name,
email:email,
username: username,
password: password,
coins:coins.encryptcoins('0'),
joindate:getDate()
});
User.createUser(newUser, function(err, user){
if(err) throw err;
});
req.flash('success_msg', 'You are registered and can now login');
res.redirect('/user/login');
}
});
}
});
})
EDIT:
user Schema
var mongoose = require('mongoose');
var bcrypt = require('bcryptjs');
// User Schema
var UserSchema = mongoose.Schema({
username: {
type: String,
index:true,
required:true
},
password: {
type: String,
required:true
},
email: {
type: String,
required:true
},
name: {
type: String,
required:true
},
coins: {
type:String,
required:true
},
joindate: {
type:String,
required:true
},
orders: {
type:Array,
required:false
}
},{collection:'Users'});
var User = module.exports = mongoose.model('User', UserSchema);
module.exports.createUser = function(newUser, callback){
bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash(newUser.password, salt, function(err, hash) {
newUser.password = hash;
newUser.save(callback);
});
});
}
module.exports.getUserByUsername = function(username, callback){
var query = {username: username};
User.findOne(query, callback);
}
module.exports.getUserById = function(id, callback){
User.findById(id, callback);
}
module.exports.comparePassword = function(candidatePassword, hash, callback){
bcrypt.compare(candidatePassword, hash, function(err, isMatch) {
if(err) throw err;
callback(null, isMatch);
});
}
this is the whole code
var express = require('express');
var router = express.Router();
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var User = require('../models/users');
const ensureLoggedIn = require('connect-ensure-login').ensureLoggedIn();
const ensureLoggedOut = require('connect-ensure-login').ensureLoggedOut();
var Coins = require('../models/coins');
// Register
router.get('/register',ensureLoggedOut, function(req, res){
res.render('user/register');
});
// Login
router.get('/login',ensureLoggedOut, function(req, res){
res.render('user/login');
});
// Register User
router.post('/register', function(req, res){
var name = req.body.name;
var email = req.body.email;
var username = req.body.username;
var password = req.body.password;
var password2 = req.body.password2;
console.log(email)
console.log(username)
// Validation
req.checkBody('name', 'Name is required').notEmpty();
req.checkBody('email', 'Email is required').notEmpty();
req.checkBody('email', 'Email is not valid').isEmail();
req.checkBody('username', 'Username is required').notEmpty();
req.checkBody('password', 'Password is required').notEmpty();
req.checkBody('password2', 'Passwords do not match').equals(req.body.password);
//Error handling
var errors = [];
var valErrors = req.validationErrors()
//check for errors in Req.validation and push them to errors Array
if(valErrors){
for (var i = 0; i < valErrors.length; i++) {
errors.push(valErrors[i])
}
}
//check if the username submitted exists in the database
User.findOne({'username':username}, function (err, user) {
if(user)
{
errors.push({msg:"username is already in use!"})
res.render('user/register',{
errors:errors
});
}
//if the username is not in use already check if the email is in
//use
else {
User.findOne({'email':email}, function (err, user) {
if(user){
errors.push({msg:'email is already in use !'})
res.render('user/register',{
errors:errors
});
} //if the email doesnt exists too then register this //user
else{
var coins = new Coins()
var newUser = new User({
name: name,
email:email,
username: username,
password: password,
coins:coins.encryptcoins('0'),
joindate:getDate()
});
User.createUser(newUser, function(err, user){
if(err) throw err;
});
req.flash('success_msg', 'You are registered and can now login');
res.redirect('/user/login');
}
});
}
});
})
passport.use(new LocalStrategy(
function(username, password, done) {
User.getUserByUsername(username, function(err, user){
if(err) throw err;
if(!user){
return done(null, false, {message: 'Unknown User'});
}
User.comparePassword(password, user.password, function(err, isMatch){
if(err) throw err;
if(isMatch){
return done(null, user);
} else {
return done(null, false, {message: 'Invalid password'});
}
});
});
}));
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.getUserById(id, function(err, user) {
done(err, user);
});
});
router.post('/login',
passport.authenticate('local', {successReturnToOrRedirect: '/', failureRedirect:'/user/login',failureFlash: true}),
function(req, res) {
res.redirect('/');
});
router.get('/logout',ensureLoggedIn, function(req, res){
req.logout();
req.session.destroy();
res.redirect('/');
});
module.exports = router;
function getDate(){
var d = new Date()
return ("date: "+d.getDate()+"/"+(d.getMonth()+1)+"/" +d.getFullYear() + " time GMT+1: "+(d.getHours()+1)+":"+(d.getMinutes())).toString()
}
// replaced with Ensure loging in library !
// function ensureLoggedIn(req, res, next) {
// if(req.user){
// return next()
// }else{
// res.redirect('/user/login');
// }
// }
// function ensureLoggedOut(req, res, next) {
// if(!req.user){
// return next()
// }else{
// res.redirect('/');
// }
// }
In general, for a logical unit of work send to a Database Management System (DBMS) (i.e., MongoDB server), it is imperative to group the individual operations in a single transaction. This way, you can avoid inconsistencies that might result from concurrent user creation in your database.
To be more precise, in your project the registration process checks for the following:
Check if email exists
Check if username exists
If queries 1 and 2 returned an empty result set, register a new user
In essence, those 3 steps need to take place in an atomic fashion, which means that they occur as a single logical unit (Transaction). If not, in the extreme case that 2 concurrent clients try to register users with the same usernames, then your database will result with two users with the same username.
Therefore, you should update your code to do the following:
Initiate a transaction
Check for users with the given email (user_email) and or usernamae (user_name)
If the query of step 2 returned a user, then rollback the transaction; Otherwise, insert a new user with user_email and user_name.
Commit Transaction
I am not sure whether MongoDB supports Transactional Consistency, and this is one of the reasons that I suggested using an RDBMS. Also, if it doesn't, I am sure that you can figure out a schema that identifies a single user based on email/username and try to perform the registration as a transaction.
Finally, it is considered good practice to have most of the processing take place in the DBMS side with the use of Stored Procedures.
I hope this helps.

Nodejs/Express - Error: Can't set headers after they are sent

Pretty new to node/express. I'm checking to see if the user (via the username) already exists in the database that one wants to register to, giving an error if they do already exist.
When I use curl to try to set it off intentionally, I get the following error:
Error: Can't set headers after they are sent.
I know already that the first check I do to ensure that all the fields are filled in works correctly, and provides no issues with headers being set multiple times.
Any help would be greatly appreciated.
(My relevant code is below. If you need anything else, feel free to say so!)
router.post('/register', function(req, res, next) {
if(!req.body.username || !req.body.password){
return res.status(400).json({ message: 'Please fill out all fields.' });
}
User.count({ username: req.body.username}, function(err, count){
console.log(count);
if(count > 0) {
return res.status(400).json({message: 'This user already exists!' });
}
});
var user = new User();
user.username = req.body.username;
user.setPassword(req.body.password);
user.save(function(err) {
if(err) { return next(err); }
return res.json({ token: user.generateJWT()});
});
});
When you are returning inside User.count and user.save, you are returning only from inside the callbacks but not the entire method.
Its a good practice to send a response in just one place. At the end of the method. Before that evaluate your conditions and set the response code and response message in some variable. Which you can use to send the response as a final step.
Try this as a workaround for now:
router.post('/register', function(req, res, next)
{
if(!req.body.username || !req.body.password)
{
return res.status(400).json({ message: 'Please fill out all fields.' });
}
User.count({ username: req.body.username}, function(err, count)
{
console.log(count);
if(count > 0)
{
return res.status(400).json({message: 'This user already exists!' });
}
else
{
var user = new User();
user.username = req.body.username;
user.setPassword(req.body.password);
user.save(function(err)
{
if(err)
{
return next(err);
}
return res.json({ token: user.generateJWT()});
});
}
});
});
Put all your code in the callback function of User.count, otherwise the two part of code are executed
router.post('/register', function(req, res, next) {
if(!req.body.username || !req.body.password){
return res.status(400).json({ message: 'Please fill out all fields.' });
}
User.count({ username: req.body.username}, function(err, count){
console.log(count);
if(count > 0) {
return res.status(400).json({message: 'This user already exists!' });
}
var user = new User();
user.username = req.body.username;
user.setPassword(req.body.password);
user.save(function(err) {
if(err) { return next(err); }
return res.json({ token: user.generateJWT()});
});
});
});

exit block of code with express and node

Here is my code
// on all routes that end with "users", do the following
router.route('/users')
.post(function(req, res, next) {
var user = new User();
user.username = req.body.username;
user.password = req.body.password;
User.find({username : user.username}, function(err, results){
if (results.length > 0) {
//if (err) res.send(err);
console.log('User exists: ', user.username);
res.send('User exists');
next();
}
});
user.save(function(err) {
if (err)
res.send(err);
res.json({
message: 'Created user.',
username: req.body.username,
password: req.body.password
});
});
})
User is just a Mongoose schema.
If the username is found in that first callback, I want to send a basic response of "User Exists", then exit. Right now I get an error because it moves on to the user.save bit and tries to write more info to the response which has already ended.
How can I exit the User.find block and the .post block altogether? In a normal C-like language I'd simply do return; but doing so only exits just the User.find block.
Thanks for any help
Try to write another case in else condition and return the callback like this
router.route('/users')
.post(function(req, res, next) {
var user = new User();
user.username = req.body.username;
user.password = req.body.password;
User.find({username : user.username}, function(err, results){
if (results.length > 0) {
//if (err) res.send(err);
console.log('User exists: ', user.username);
res.send('User exists');
return next();
}else{
user.save(function(err) {
if (err)
return res.send(err);
return res.json({
message: 'Created user.',
username: req.body.username,
password: req.body.password
});
});
}
});
})
In your current implementation, if it finds the user, it will run whatever you have written in your callback. You happen to have next() inside your callback. next() will exit this route and find the next route that would match. That means whatever you wrote under, won't be executed (I'm talking about the user.save part.
Anyways, to answer your question, if you want it to exit the User.find block and the .post all together, then just put the next() method below and outside your if statement.
I hope this helps.

Unexpected results with MongooseJS findOne()

Using Mongoose as an ODM with NodeJS, but not fully understanding how the error handling works. It works, but doesn't look right, and isn't in line with the documentation, so I'm worried that going down this road will haunt me later on.
For example, here is a basic signin route:
app.post('/signin', function(req, res){
var email = req.body.email;
var password = req.body.password;
mongoose.model('User').findOne({
email: email,
password: password
}, function(err, user){
if (err){
console.log('Database Error')
return res.json({error: 'Database Error'})
} else {
if (!user) {
console.log('User not found.');
return res.json({error: 'Email and/or password incorrect.'})
} else {
console.log('User ' + user.email + ' found. Logging in.');
res.json({
token: jwt.sign({}, 'top-secret', {subject: user}),
data: data[user]
})
}
}
})
})
I'm especially worried about:
if (err) {
//do something
} else {
if (!user){
//do something else
} else {
//log the user in
}
}
Haven't really used Mongo before today, but this feels like a lot of conditional error handling. Is there something that I'm not understanding properly here?
Was going to post as a comment but it was easier to paste this as an answer..
You can simplify the if-else nesting since you are returning at the end of each conditional, like so:
app.post('/signin', function (req, res) {
var email = req.body.email;
var password = req.body.password;
mongoose.model('User').findOne({
email: email,
password: password
}, function (err, user) {
if (err) {
console.log('Database Error');
return res.json({error: 'Database Error'});
}
if (!user) {
console.log('User not found.');
return res.json({error: 'Email and/or password incorrect.'});
}
console.log('User ' + user.email + ' found. Logging in.');
res.json({
token: jwt.sign({}, 'top-secret', {subject: user}),
data: data[user]
});
});
});

How can handle the error in node.js?

models/user.js
var User = module.exports = mongoose.model('User',UserSchema);
module.exports.getUserByUsername = function(username, callback){
var query = {username:username};
User.findOne(query, callback);
}
module.exports.createUser = function(newUser, callback){
User.getUserByUsername(newUser.username,function(err,username){
if(err) throw err;
// username already exists
if(username){
// How can handle this error?
console.log('username already exists');
} else{
bcrypt.hash(newUser.password, 10, function(err,hash){
if(err) throw err;
newUser.password = hash;
newUser.save(callback);
});
}
});
}
router/users.js
router.post('/register',function(req,res,next){
var username = req.body.username;
var password = req.body.password;
var newUser = new User({
username:username,
password:password
});
req.checkBody('username','Username field is required').notEmpty();
req.checkBody('password','Password field is required').notEmpty();
var errors = req.validationErrors();
if(errors){
res.render('register',{
errors: errors,
username: username,
password: password
});
} else{
User.createUser(newUser,function(err,user){
if(err) throw err;
req.flash('success','You are now registered and may log in');
res.location('/');
res.redirect('/');
});
}
});
above codes are about a user registration code.
in models/user.js
I made a function 'createUser' that get a new User, and find whether it already exists. However, I don't know how to throw custom error, "User already exists"..
What should I do?
I tried like this :
// username already exists
if(username) {
callback(new Error('username already exists'));
}
but It didn't work!
To throw an error with a message "username already exsits", what should I do?
Thank you for reading. :)
p.s)
models/user.js -> createUser function was defined.
routes/users.js -> createUser function was attached to work.
You could handle this by just following Node best practices and return an error in the callback
module.exports.createUser = function(newUser, callback){
User.getUserByUsername(newUser.username, function(err, username) {
if(err) throw err;
if(username){
callback('username already exists', null); // callback(error, data) ...
......
Then catch it
router.post('/register',function(req,res,next){
... get data
if(errors){
res.render('register',{
errors: errors,
username: username,
password: password
});
} else {
User.createUser(newUser,function(err,user){
if(err) {
res.render('register',{
errors: err, // username already exists
username: username,
password: password
});
} else {
req.flash('success','You are now registered and may log in');
res.location('/');
res.redirect('/');
}
});
}
});

Categories