Node JS Mongoose Async Callbacks - node.js

I've got this piece of code that I seem to be getting in a bit of a muddle with.
What it does is create users. Now, if a user has a company, then that company should be created along with the user and linked accordingly. If the company already exists, then it shouldn't be created and it shouldn't be attributed to the user.
First the code looks for a company, if it can't find one then one is created. Life is good. But if I were to add an else to my "if (!company)" check i would be duplicating the majority of my create user code. I also believe I can't check the company and then run the user creation synchronously as I would usually do in a different language. Hence i'm getting a little stuck..
module.exports = {
postUsers: (req, res) => {
'use strict'
Company.findOne({name: req.body.company}, (err, company) => {
if (err) {
Logger.error(err)
return res.send(500, err)
}
if (!company) {
// only attribute a company if one doesn't exist
// don't want users to assign themselves to existing companies automatically
// need approval in place from an existing company member
let newCompanyToAdd = new Company({
name: req.body.company
})
newCompanyToAdd.save(err => {
if (err) {
Logger.error(err)
return res.send(500, err)
}
let user = new User({
username: req.body.username,
password: req.body.password,
firstname: req.body.firstname,
lastname: req.body.lastname,
company: newCompanyToAdd.id
})
user.save(err => {
if (err) {
return res.send(500, err)
}
res.status(200).json({ message: 'New User Added' })
})
})
}
})
}
EDIT#
postUsers: (req, res) => {
'use strict'
let user = new User({
username: req.body.username,
password: req.body.password,
firstname: req.body.firstname,
lastname: req.body.lastname
})
Company.findOne({name: req.body.company}, (err, company) => {
if (err) {
Logger.error(err)
return res.send(500, err)
}
if (!company && req.name.company !== undefined) {
// only attribute a company if one doesn't exist
// don't want users to assign themselves to existing companies automatically
// need approval in place from an existing company member
let newCompanyToAdd = new Company({
name: req.body.company
})
newCompanyToAdd.save(err => {
if (err) {
Logger.error(err)
return res.send(500, err)
}
user.company = newCompanyToAdd._id
})
}
})
user.save(err => {
if (err) {
return res.send(500, err)
}
res.status(200).json({ message: 'New User Added' })
})
}

I'm not totally sure I understand the overall goal. But seems like you're worried about having the add user code be replicated because you need to add the user regardless of if the company already exists or not. Is there any reason you can't save the user first, and in the callback, conditionally create the company if necessary?

Related

I Need To Avoid The Duplicate Entries In DataBase

i am trying to add some data inside my MongoDB database, but i am not able to remove duplicate entries.
please help me to do so.
i am using node js and mongoose.
app.post('/', function (req, res) {
const newUser = new Newsletter ({
fname: req.body.fname,
lname: req.body.lname,
message: req.body.message,
email: req.body.email
});
newUser.save(function (err) {
if (!err) {
res.render("success");
} else {
const errCode = res.statusCode;
res.render("failure", { errStatusCode: errCode });
}
})
})

Fetch data from MongoDB when the user register

I am learning MEAN stack environment and I have a question.
I have a registration page, which registers the user in MongoDB:
// register.component.ts
register(){
this.http.post('http://localhost:3001/register', this.input)
.subscribe(
( next: any) => {
// TO-DO Success event
},
( error: any) => {
// TO-DO Error event
});
}
// app.js
app.post('/register', function(req, res){
db.collection('users').insertOne({
prenom : req.body.prenom,
nom: req.body.nom,
email : req.body.email,
password : req.body.password
})
})
It works pretty well, the problem is that for the connection, I use the _id:
// login.component.ts
login(id: string){
this.http.get('http://localhost:3001/login/' + id).toPromise().then((data: any) => {
this.users = data
})
sessionStorage.setItem('id', id)
}
// app.js
app.get('/login/:id', function(req, res){
db.collection('users').findOne({ email: ObjectId(`${req.params.id}`)}, function(err, user){
if (err) throw err;
if (!user) {
console.log('User not found')
}
else if (user)
{
console.log('Found user: ' + user.prenom)
}
})
})
How to make sure that when the user registers, it returns his _id directly, and like that I can put him in session:
sessionStorage.setItem('id', id)
The db.collection.insertOne() function returns the inserted document, see here. This means you can do a callback or async/await (whichever you prefer) for your insertOne() function and then return the _id by using the Express function res.json(). In your frontend, you'll then get whatever content you put into res.json() as a response. Happy coding! :)

Get data from findOne mongoose inside another findOne function

I am trying to get the candidate or HR (user roles) object using mongoose and nodejs. I have a user and both roles are derived from it.
when trying to connect using a UNIQUE username and a password. A user object will be sent as a result. I want to also send candidate/ or HR that are linked to that user.
I am passing the user object by reference to the candidate/HR schema:
const candidateSchema = new Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
index: true,
},
fullName: String,
profilePhoto: String,
birthday: Date,
I need to get the candidate object of the user that i get inside the exec() function. save it in a variable and send it as a res to signin function
app.post("/api/auth/signin", (req, res) => {
User.findOne({
username: req.body.username,
})
.populate("roles", "-__v")
.exec((err, user) => {
if (err) {
res.status(500).send({ message: err });
return;
}
const candi = candidat.findOne({ user: user }).exec((err, candidate) => {
//I want to save the candidate var
}));
//console.log("res",candi);
.....
});
A simple solution will be to wrap your code inside a promise and resolve whatever you want to store to variable while reject when you want to send error.
But its recommended to break down your code to multiple async functions and await them instead of using callback exec functions.
app.post("/api/auth/signin", async (req, res) => {
try{
let response = await new Promise((resolve,reject)=>{
User.findOne({
username: req.body.username,
})
.populate("roles", "-__v")
.exec((err, user) => {
if (err) {
//REJECT ERROR
reject(err);
return;
}
const candi = candidat.findOne({ user: user }).exec((err, candidate) => {
//RESOLVE YOUR CANDIDATE
resolve(canditate);
}));
//console.log("res",candi);
.....
});
.... your rest of code
})
res.send(response) // or required json
}catch(err){
res.status(500).send({ message: err });
}
}

Node.js - Check if user exists

I'm using NodeJS and passport to let users create an account before they can see results of a quiz they've just taken. My challenge is I need to confirm the username is available before the page refreshes because the user will lose their results if this happens.
Again: I need to verify the username is not taken prior to refreshing.
I think I'm close but it is not working. How would I change my code to handle this challenge?
Currently if the user name is taken it returns an error on trying to create an account and the user ends up on the /failpage as shown below.
app.post('/quiz', usernameToLowerCase, emailToLowerCase, function(req, res) {
User.findOne({
username: req.body.username
}, function(err, user) {
if (err) {
alert(err)
if (user) {
alert('this username is already taken. Please choose another.')
console.log('there was a user');
return false;
}
}
});
var user = new User({
username: req.body.username,
email: req.body.email,
password: req.body.password,
})
user.save(function(err) {
console.log('this is the problem' + ' ' + err)
if (err) {
return res.redirect('/failpage')
}
req.logIn(user, function(err) {
if (err) {
console.log(err);
}
console.log('all looks good')
res.redirect('/results');
});
});
});
Solved it with this if anyone else is trying to do the same thing:
in app.js
app.get('/usercheck', function(req, res) {
User.findOne({username: req.query.username}, function(err, user){
if(err) {
console.log(err);
}
var message;
if(user) {
console.log(user)
message = "user exists";
console.log(message)
} else {
message= "user doesn't exist";
console.log(message)
}
res.json({message: message});
});
});
In js
$('#usercheck').on('change', function() {
$.get('/usercheck?username='+$('#usernameValue').val().toLowerCase(), function(response) {
$('#usernameResponseHidden').text(response.message)
if ($('#usernameResponseHidden').html() === "user exists"){
$('#usernameResponse').text('That username is taken. Please pick another')
}
To solve your problem I think you need to routes. At least a app.get('/quiz') which returns a boolean on if the user exists or not. The section User.findOne can be sent in that route instead. You just need to make a request using ajax when he looses focus of the username field of your form, and display a notification if the name is available or not.

How to Async Mongoose Controller

Trying to configure a SignUp() controller that can update multiple (separate) user accounts when a referral code is provided by the user.
Basic Flow:
Verify email doesn't already exist in system
Find the driver w/ userID matching the rider's refCode (FindOneAndUpdate)
If Found: Add the userID of each user to the other users [clients] list
Only need to do a refCode match if isRider
If any of those fail... Return the specific error to the client/user
This does not work. But essentially, this is what I'm trying to accomplish...
// POST `/signup` (Create a new local user)
export function signUp(req, res, next) {
const newUser = new User({
email: req.body.email,
password: req.body.password,
profile: {
userID: req.body.userID,
refCode: req.body.refCode,
isRider: req.body.isRider
}
});
User.findOne({ email: req.body.email }, (findErr, foundUser) => {
if (foundUser) {
return res.status(409).send('This e-mail address already exists');
}
// riders must link to a driver
if (req.body.isRider) {
// find driver + add rider ID to clients
return User.findOneAndUpdate({ 'profile.userID': req.body.refCode }, { $push: { clients: newUser.profile.userID }}).exec()
.then((err, foundDriver) => {
if (err) {
return res.status(409).send('Error searching for driver');
} else if (!foundDriver) {
return res.status(409).send(`We can't find your driver (${req.body.refCode})!`);
}
// add driver ID to rider's clients
newUser.clients = [req.body.refCode];
return newUser.save((saveErr) => {
if (saveErr) return next(saveErr);
return req.logIn(newUser, (loginErr) => {
if (loginErr) return res.sendStatus(401);
return res.json(newUser.profile);
});
});
});
}
return newUser.save((saveErr) => {
if (saveErr) return next(saveErr);
return req.logIn(newUser, (loginErr) => {
if (loginErr) return res.sendStatus(401);
return res.json(newUser.profile);
});
});
});
}
Tried to configure it as a pure promise but no luck. Most of the examples out there all seem different to me... Also could not figure out how to handle/throw specific errors using the mongoose docs.
Greatly appreciated if anyone can lend a hand, Thx!
UPDATE:
Ippi's answer helped a ton - Thx!
This does the trick. Remember to return null from .then() after the req.login stuff to avoid warnings - Any tips on how to improve this are appreciated - Thx!
const createUser = (foundUser) => {
if (foundUser) { throw new Error('This e-mail address already exist.'); }
if (!req.body.isRider) { return newUser.save(); }
return User.findOneAndUpdate({ 'profile.userID': req.body.refCode.toLowerCase() }, { $push: { clients: newUser.profile.userID }}).exec()
.then((driver) => {
if (!driver) { throw new Error('We can\'t find your driver.'); }
newUser.clients = [req.body.refCode];
return newUser.save();
})
.catch(() => { throw new Error('There was a database error.'); });
};
User.findOne({ email: req.body.email }).exec()
.then(createUser)
.then((user) => {
if (user.profile) {
req.logIn(user, (loginErr) => {
if (loginErr) return res.sendStatus(401);
return res.status(200).send({ profile: user.profile, clients: user.clients });
});
} else { res.status(409); }
return null;
})
.catch((err) => { return res.status(409).send(err.message); });
function signUp(req, res, next) {
return new Promise((resolve, reject) => {
const newUser = new User({
email: req.body.email,
password: req.body.password,
profile: {
userID: req.body.userID,
refCode: req.body.refCode,
isRider: req.body.isRider
}
});
User.findOne({ email: req.body.email }, (findErr, foundUser) => {
if (foundUser) {
// return res.status(409).send('This e-mail address already exists');
reject('This e-mail address already exists');
}
// riders must link to a driver
if (req.body.isRider) {
// find driver + add rider ID clients
return User.findOneAndUpdate({ 'profile.userID': req.body.refCode }, { $push: { clients: newUser.profile.userID } }).exec()
.then((err, foundDriver) => {
if (err) {
// return res.status(409).send('Error searching for driver');
reject('Error searching for driver');
} else if (!foundDriver) {
// return res.status(409).send(`We can't find your driver (${req.body.refCode})!`);
reject(`We can't find your driver (${req.body.refCode})!`);
}
// add driver ID to rider's clients
newUser.clients = [req.body.refCode];
newUser.save((saveErr) => {
if (saveErr)
// next(saveErr);
reject(saveErr);
req.logIn(newUser, (loginErr) => {
if (loginErr)
// return res.sendStatus(401);
reject('401');
// return res.json(newUser.profile);
resolve(newUser.profile);
});
});
});
}
newUser.save((saveErr) => {
if (saveErr)
// return next(saveErr);
reject(saveErr);
req.logIn(newUser, (loginErr) => {
if (loginErr)
// return res.sendStatus(401);
reject(loginErr);
// return res.json(newUser.profile);
resolve(newUser.profile);
});
});
});
});}
This is how I would do it. I couldn't be bothered to try with express or the login (you need to replace console.log with res.status().json()) and I might have done some other blunder in the logic with the driver. But other than that I tested it with local mongo and it probably works and if nothing else it's a little bit more concise.
let updateUser = user => {
if (user){ throw new Error("USER_EXIST"); }
if (!req.body.isRider) { return newUser.save() }
return User.findOneAndUpdate({ 'profile.userID': req.body.refCode },{ $push: { clients: newUser.profile.userID }}).exec()
.then(driver => {
if (!driver) { throw new Error("NO_DRIVER");}
newUser.clients.push(req.body.refCode);
return newUser.save();
});
}
User.findOne({ email: req.body.email }).exec()
.then(updateUser)
.then(req.logIn) // newUser.save() response is passed in as is (I have not tested this line.)
.then( ()=>{ return console.log('profile', newUser.profile); })
.catch( Error, err => {
if (err.message == "USER_EXISTS") return console.log ("This e-mail address already exist." );
if (err.message == "NO_DRIVER") return console.log ("We can't find your driver." );
throw err;
});
Something worth remembering:
Callback calls or res.send should always go in the last then / catch. Calling res.send in middle of chains leads to trouble.

Resources