Display Flash Error Messages to User After Mongoose Model Save - node.js

I have a page index.js which has a form to add users, and beside it a list of users in the database.
/routes/index.js
var express = require('express');
var router = express.Router();
var User = require('../schemas/user');
router.post('/create', function(req, res, next) {
var user = new User({
username: req.body.username,
email: req.body.email,
password: req.body.password
});
user.save(function(err) {
if (err) {
console.log('user save error ' + err.errmsg);
return res.json(err.errmsg);
}
res.redirect('/');
});
});
/* GET home page. */
router.get('/', function(req, res, next) {
User.find(function (err, users) {
if (err) {
console.log('get error ' + err);
//return res.sendStatus(500);
}
res.render(
'index',
{
userList : users
}
);
});
});
module.exports = router;
/schemas/user.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var userSchema = new Schema({
username: { type: String, required: true, unique: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
group: String,
created_at: Date,
updated_at: Date
});
var User = mongoose.model(
'User',
userSchema
);
module.exports = User;
Here is my view:
/views/index.pug
extends layout
block content
h1= title
p Welcome to #{title}
.container
.row
.col-sm
h1 Create User
form(
method='POST'
action='/create'
)
.form-group
label(for='username') Username:
input#username.form-control(
type='text',
placeholder='Enter username...',
name='username'
)
if usernameError
p.error= usernameError
.form-group
label(for='password') Password:
input#password.form-control(
type='password',
placeholder='Enter password...',
name='password'
)
if passwordError
p.error= passwordError
.form-group
label(for='email') Email:
input#email.form-control(
type='email',
placeholder='Enter email...',
name='email'
)
if emailError
p.error= emailError
button.btn.btn-primary(
type='submit',
) Submit
.col-sm
h2 User List
ul
each user in userList
li= user.username
As you can see, I have some conditionals in my index.pug file. What I want to do is if an error occurs I want to assign a message to a variable based on the error type (i.e. username already taken, or password too short) and pass that variable over to my pug view. The view will then render the message if the proper variable is set. Can somebody help me out? I'm mostly struggling with the fact that I also have to render the list of users, if I try to find users within the error catching part of the post node complains about headers already being set, i.e. If I have my router.post function like so:
router.post('/create', function(req, res, next) {
var user = new User({
username: req.body.username,
email: req.body.email,
password: req.body.password
});
user.save(function(err) {
if (err) {
console.log('user save error ' + err.errmsg);
User.find(function (err2, users) {
if (err2) {
console.log('get error ' + err2);
//return res.sendStatus(500);
}
res.render(
'index',
{
userList : users,
usernameError: err.errmsg
}
);
});
}
res.redirect('/');
});
});
Then I expect to see the usernameError message filled in my view but instead I get an error from the node server:
user save error E11000 duplicate key error collection: test.users index: username_1 dup key: { : "John" }
POST /create 302 71.995 ms - 46
Error: Can't set headers after they are sent.

username: { type: String, required: true, unique: true },
it's because unique true.
Error: Can't set headers after they are sent.
Error because you haven't return error. Whenever error occure simply return like
if (err) return next(err)
provided that you are using express centralized error handler
check last lines of your app.js.All errors from next(err) goes here
app.use(function(err, req, res, next) {
console.error( err);
..............
});

Related

How can i access values from database

How can i access for example username and put it in profile page ?
model/db.js
const mongoose = require('mongoose');
const stDB = mongoose.Schema({
username : {
type: String,
required: true
},
email : {
type: String,
required: true
},
password : {
type: String,
required: true
}
});
module.exports = mongoose.model('db', stDB);
views/profiles/instructor.hbs
<h5>I want access username from db and put it here!</h5>
index.js
const users = require('../model/db'); // db that username stored in it (model/db.js)
//instructor
router.get('/profiles/instructor', function (req, res, next) {
res.render('./profiles/instructor', {
title: 'Instructor'
});
});
router.post('/signup', function (req, res, next){
const newUser = new users({
username : req.body.username,
email : req.body.email,
password : req.body.password,
});
users.findOne({email : req.body.email}, (err, doc)=>{
if(err){
console.log('ERR while getting username =>' + err);
return ;
}
if(doc){
res.send('this email is already registered before!');
return ;
}
newUser.save((err, doc)=>{
if(err){
console.log('err' + err)
}else{
console.log(doc)
res.redirect('/login')
}
});
});
// etc.....

MissingUsernameError: No username was given - Unsure where i'm going wrong

I'm using Node.js with Mongoose and Passport trying to get the user to save to the DB but keep encountering the error where No Username was given. I can get it to save if just using using username and password but as soon as I try to add more fields I get the issue. This is the code I have:
app.js
const userSchema = new mongoose.Schema ({
firstname: String,
lastname: String,
username: String,
password: String,
userLevel: {type: Number},
profileImage: String,
title: String
});
//ENABLE PASSPORT LOCAL
userSchema.plugin(passportLocalMongoose, {
selectFields: ' firstname lastname username password userLevel profileImage title'
});
//CREATE NEW model
const User = new mongoose.model("User", userSchema);
passport.use(User.createStrategy());
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
app.get('/control', (res, req) => {
if (req.isAuthenticated()) {
res.render('control');
} else {
res.redirect('/login')
}
});
app.post("/register", (req, res) => {
User.register(new User(
{firstname: req.body.firstname},
{lastname: req.body.lastname},
{username:req.body.username},
{userLevel: 1},
{profileImage:"not set"},
{title:"not set"}
),
req.body.password,
(err, user) => {
if (err) {
console.log(err);
console.log(req.body.username);
} else {
passport.authenticate('local')(req, res, () =>{
res.redirect('/control');
});
}
});
});
Figured it out! I was using individual objects rather that just the one object :
User.register((
{firstname: req.body.firstname,
lastname: req.body.lastname,
username: req.body.username,
userLevel: 1,
profileImage:"not set",
title:"not set"
}),
req.body.password,
(err, user) => {
if (err) {
console.log(err);
console.log(req.body.username);
} else {
passport.authenticate('local')(req, res, () =>{
res.redirect('/control');
});
}
});
});

How to update user details according to this model and controller in Node.js express

I am trying to update user data in the settings page. Where he/she can change all details like name, last name, birthday and so on. Here is the auth controller:
module.exports = {
async CreateUser(req, res) {
const schema = Joi.object().keys({
username: Joi.string()
.min(4)
.max(10)
.required(),
email: Joi.string()
.email()
.required(),
firstName: Joi.string()
.required(),
lastName: Joi.string()
.required(),
position: Joi.string()
.required(),
password: Joi.string()
.min(5)
.required(),
});
const { error, value } = Joi.validate(req.body, schema);
if (error && error.details) {
return res.status(HttpStatus.BAD_REQUEST).json({ msg: error.details })
}
const userEmail = await User.findOne({
email: Helpers.lowerCase(req.body.email)
});
if (userEmail) {
return res
.status(HttpStatus.CONFLICT)
.json({ message: 'Email already exist' });
}
const userName = await User.findOne({
username: Helpers.firstUpper(req.body.username)
});
if (userName) {
return res
.status(HttpStatus.CONFLICT)
.json({ message: 'Username already exist' });
}
return bcrypt.hash(value.password, 10, (err, hash) => {
if (err) {
return res
.status(HttpStatus.BAD_REQUEST)
.json({ message: 'Error hashing password' });
}
const age = moment().diff(moment([value.byear, value.bmonth - 1, value.bday]), 'years');
const body = {
username: Helpers.firstUpper(value.username),
email: Helpers.lowerCase(value.email),
firstName: value.firstName,
lastName: value.lastName,
position: value.position,
password: hash,
};
User.create(body)
.then(user => {
const token = jwt.sign({ data: user }, dbConfig.secret, {
expiresIn: '5h'
});
res.cookie('auth', token);
res
.status(HttpStatus.CREATED)
.json({ message: 'User created successfully', user, token });
})
.catch(err => {
res
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.json({ message: 'Error occured' });
});
});
},
User model
const userSchema = mongoose.Schema({
username: { type: String },
email: { type: String },
isVerified: { type: Boolean, default: false },
firstName: { type: String },
lastName: { type: String },
position: { type: String },
password: { type: String },
I guess I shoud have a route like this:
router.post('/user/settings', AuthHelper.VerifyToken, user.editUser);
How should it look like editUser controller according to above CreateUser function? I am using Angular in the front-end. But I think it doesn't matter. I assume 90 percent should be the same as CreateUser but what exactly should be changed so the user can update his/her details in settings form and change data in the model?
So you want to update some of user's fields (such as firstName, lastName and etc.), not replacing the whole information. Then you might want to get the current user's data first and then update only those allowed fields.
Please find the sample code below.
/**
* User router
*/
router.put('/user/:userId', AuthHelper.VerifyToken, user.editUser);
// This function will be triggered when Express finds matching route parameter
router.param('userId', function (req, res, next, id) {
User.findOne(id, function (err, user) {
if (err) {
next(err);
} else if (user) {
// When it finds user information, bind that to request object, which will be used in the other middlewares.
req.user = user;
next();
} else {
next(new Error('failed to load user'));
}
});
});
/**
* User controller
*/
exports.editUser = (req, res, next) => {
let { user } = req;
// You pick only allowed fields from submitted body
const allowedFields = { firstName: req.body.firstName, lastName: req.body.lastName, birthday: req.body.birthday };
// Override the current user data with new one
user = Object.assign(user, allowedFields);
user.save((err, savedUser) => {
if (err) {
return next(err);
}
res.json(savedUser.toJSON());
});
};

Best way to validate uniqueness of a field Mangoose Node

When registering a new user, I want to check email for uniqueness. I am using body parser to make sure all fields are not empty, but how am I to check that the input email is not used by anyone else and to immediately output the message for a user?
The technology stack is Node.js, mongoose, body-parser module, mongodb
Here's the route file:
router.post('/register', function(req, res){
let name = req.body.name;
let email = req.body.email;
req.checkBody('name', 'Name field is empty!').notEmpty();
req.checkBody('email', 'Email field is empty!').notEmpty();
req.checkBody('email', 'Invalid email format').isEmail();
var errors = req.validationErrors();
if(errors){
res.render('register', {
errors:errors
});
} else {
let newUser = new User({
name: name,
email: email
});
After looking through similar questions, I found a way to use a pre save in my model file, but I don't know how to sisplay the error to the user as a part of the errors array (see above)
Any help will be highly appreciated!
You can achieve both from the Mongoose model and the Register() method.
The user model should be like this:
var mongoose = require('mongoose');
var UserSchema = new mongoose.Schema({
email: {
type: String,
lowercase: true,
unique: true,
sparse: true
},
password: {
type: String,
required: true
},
},
{
timestamps: true
});
module.exports = mongoose.model('User', UserSchema);
This will ensure that emails are unique. Then at the register method, you do this:
exports.register = function(req, res, next){
let name = req.body.name;
let email = req.body.email;
User.findOne({email: email}, function(err, existingUser){
if(err){
return res.status(500).json(err);
}
if(existingUser){
return res.status(422).json('Email address is already registered.');
}
else {
var user = new User({
username : username,
email: email,
password: password
});
user.save(function(err, user){
if(err){
return next(err);
}
var userInfo = setUserInfo(user);
res.status(201).json({
token: 'JWT ' + generateToken(userInfo),
user: userInfo
})
});
}
});
}
}
}
Hope this helps.

Node>Expressjs res.render not redirecting

I am developing application with nodejs and express. I have login page. I am posting user data and if there is no user with that data then i want to redirect page. But res.render not working(I added comment where res.render is in my code like "//Redirect if user not found". Have no idea. Here is my code:
var mongoose = require('mongoose');
mongoose.connect("mongodb://localhost/fuatblog");
var UserSchema = new mongoose.Schema({
name: String,
email: String,
password: String,
age: Number
}),
Users = mongoose.model('Users', UserSchema);
app.post('/sessions', function (req, res) {
console.log(req.body.user.email);
console.log(req.body.user.password);
Users.find({
email: req.body.user.email,
password: req.body.user.password
}, function (err, docs) {
if (! docs.length) {
// no results...
console.log('User Not Found');
//res.status(400);
//Redirect if user not found
return res.render(__dirname + "/views/login", {
title: 'Giriş',
stylesheet: 'login',
error: 'Email or password is wrong.'
});
}
console.log('User found');
req.session.email = docs[0].email;
console.log(req.session.email);
});
return res.redirect('/Management/Index');
});
The .render method which you want to be invoke when the user is not recognized is in async code. This means that the return res.redirect('/Management/Index'); is called once the request reaches your server. But you should do that once you get the result from Users.find. I.e.:
app.post('/sessions', function (req, res) {
console.log(req.body.user.email);
console.log(req.body.user.password);
Users.find({
email: req.body.user.email,
password: req.body.user.password
}, function (err, docs) {
if (! docs.length) {
// no results...
console.log('User Not Found');
//res.status(400);
//Redirect if user not found
return res.render(__dirname + "/views/login", {
title: 'Giriş',
stylesheet: 'login',
error: 'Email or password is wrong.'
});
}
console.log('User found');
req.session.email = docs[0].email;
console.log(req.session.email);
return res.redirect('/Management/Index');
});
});

Resources