Is possible check if a record exists synchronous? - node.js

Basically I want store a user inside the database, before doing this operation I need to validate the fields of that user. So I have this structure:
exports.save = function(req, res)
{
let errors = validate(req.body);
if(!errors.length)
{
//add user
res.status(200).send(true);
}
else{
res.status(500).send("oh bad");
}
};
function validate(user)
{
let errors = [];
if (User.findOne({ email: user.email })) {
errors.push({ msg: 'Email already registered!' });
}
}
the code above simply cannot works because NodeJS handle the operation asynchronous. I can fix this "problem" doing something like:
User.findOne({ email: email }).then(user => {
if (!user) {
//add user
res.status(200).send(true);
});
but I want add the checking inside the function validate, is there a way to do that? Sorry if this question could be stupid, but I'm new to NodeJS

Since nothing's inherently making exports.save synchronous, it's probably easiest to make validate asynchronous (and let's also make it modern by using promises and async).
exports.save = async function(req, res) {
const errors = await validate(req.body);
if (!errors.length) {
//add user
res.status(200).send(true);
} else {
res.status(500).send("oh bad");
}
};
async function validate(body) {
const errors = [];
const user = await User.findOne({ email: body.email });
if (user) {
errors.push({ msg: "Email already registered!" });
}
return errors;
}

Related

controller isnt working as it is supposed to

I used MVC to make a NodeJS server and this is one of the controllers:
module.exports.create_user = async function (req, res) {
// console.log(req.body);
// console.log(req.user);
await Company.findOne({ user: req.body.user }, function (err, user) {
if (user) {
return res.redirect('/login');
}
else {
if (req.body.password == req.body.confirm_password) {
Company.create({
"country": req.body.country,
"username": req.body.user,
"password": req.body.password
});
}
else {
console.log('Passwords didnt match');
}
}
});
req.session.save(() => {
return res.redirect('/profile');
})
}
What this code supposed to do?
It searches if a user already exists; if yes, it will redirect to /login.
If no such user exists, it should create a new user and redirect to /profile.
What does this code do?
Regardless of whether the user exists or not, the code always redirects to /login. Also, a user is created in the database, so every time a new user wants to signup, the user needs to signup and then go to sign in to get access to /profile
What is the problem here which doesn't allow redirect to /profile? And how to fix it?
Let me know if you need anything else
Use username instead of user to find a user
Company.findOne({ username: req.body.user });
You are mixing callback style with async/await, await keyword does not affect on your, it will not wait until the query finished. await keyword just working when you wait for a Promise like object (then able object).
I guess you are using mongoose, the current version of mongoose supports Promise return style.
module.exports.create_user = async function (req, res) {
// console.log(req.body);
// console.log(req.user);
try {
// Use `username` instead of `user` to find a user
const user = await Company.findOne({ username: req.body.user }); // callback is not passed, it will return a Promise
if (user) {
return res.redirect('/login');
}
if (req.body.password == req.body.confirm_password) {
await Company.create({ // wait until user is created
"country": req.body.country,
"username": req.body.user,
"password": req.body.password
});
// then redirect page
req.session.save(() => {
return res.redirect('/profile');
});
} else {
console.log('Passwords didnt match');
// what happen when password didn't match
// return res.redirect('/login'); ???
}
} catch (error) {
// something went wrong
// return res.redirect('/login'); ???
}
}
passport.checkAuthentication = async function (req, res, next) {
console.log(req.user);
let auth_status = await req.isAuthenticated() ? "sucess" : "failure";
console.log(`Authentication ${auth_status}`);
// if the user is signed in, then pass on the request to the next function(controller's action)
if (await req.isAuthenticated()) {
return next();
}
// if the user is not signed in
return res.redirect('/login');
}
I did a but more work on this and possibly the controller is working fine and the problem could be in middleware. In the signup case discussed above, the middelware always logs 'Authentication failure' to console.

TypeError: newUser.find is not a function

I am very new to the MERN stack and I would like some help figuring out this error. I'm trying to check if an email is already in the database upon creating a new user. Can anyone tell me why I am getting this error?
The model and scheme
//schema
const Schema = mongoose.Schema;
const VerificationSchema = new Schema({
FullName: String,
email: String,
password: String,
date: Date,
isVerified: Boolean,
});
// Model
const User = mongoose.model("Users", VerificationSchema);
module.exports = User;
The Api
const express = require("express");
const router = express.Router();
const User = require("../Models/User");
router.get("/VerifyEmail", (req, res) => {
console.log("Body:", req.body);
const data = req.body;
const newUser = new User();
newUser.find({ email: data.email }, function (err, newUser) {
if (err) console.log(err);
if (newUser) {
console.log("ErrorMessage: This email already exists");
} else {
console.log("This email is valid");
}
});
res.json({
msg: "We received your data!!!",
});
});
module.exports = router;
The api caller using axios
const isEmailValid = (value) => {
const info = {
email: value,
};
axios({
url: "http://localhost:3001/api/VerifyEmail",
method: "get",
data: info,
})
.then(() => {
console.log("Data has been sent");
console.log(info);
})
.catch(() => {
console.log("Internal server error");
});
};
if you have body in your request, change the type of request to POST...
after that for use find don't need to create a instance of model, use find with Model
router.get("/VerifyEmail", (req, res) => {
console.log("Body:", req.body);
const data = req.body;
User.find({ email: data.email }, function (err, newUser) {
if (err) console.log(err);
if (newUser) {
console.log("ErrorMessage: This email already exists");
} else {
console.log("This email is valid");
}
});
res.json({
msg: "We received your data!!!",
});
});
I prefer to use async/await and don't use Uppercase world for routing check the article: like this
router.post("/verify-email", async (req, res) => {
try {
let { email } = req.body;
let newUser = await User.findOne({ email });
if (newUser) {
console.log("ErrorMessage: This email already exists");
} else {
console.log("This email is valid");
}
} catch (error) {
res.json({
msg: "somthing went wrong",
});
}
res.json({
msg: "We received your data!!!",
});
});
The proper way to query a Model is like so:
const User = mongoose.model('Users');
User.find({<query>}, function (err, newUser) {...
So you need to get the model into a variable (in this case User) and then run the find function directly against it, as opposed to running it against an object you instantiate from it. So this is incorrect:
const newUser = new User();
newUser.find(...
So assuming all your files and modules are linked up correctly, this should work:
const User = require("../Models/User");
User.find({<query>}, function (err, newUser) {...
The problem wasn't actually the mongoose function but I needed to parse the object being sent.
let { email } = JSON.parse(req.body);
Before parsing the object looked like {"email" : "something#gmail.com"}
and after parsing the object looked like {email: 'something#gmail.com'}
I also changed the request from 'get' to 'post' and instead of creating a new instance of the model I simply used User.find() instead of newUser.find()

deleting key from object but still showing in response

I am experimenting with node authentication, I have managed to store a username and a hashed password into my database, but I want to return the json back without the hashed password.
I am deleting the password key before sending the JSON back but the password still shows in the returned result.
router.post("/signup", async (req, res, next) => {
const user = await User.exists({ username: req.body.username });
if (user) {
const error = new Error("Username already exists");
next(error);
} else {
const newUser = new User({
username: req.body.username,
password: req.body.password,
});
try {
const result = await newUser.save();
delete result.password;
res.json(result);
} catch (err) {
res.json(err.errors);
}
}
});
the User model has a pre hook to hash the password before save:
userSchema.pre("save", async function save(next) {
const user = this;
if (!user.isModified("password")) return next();
try {
user.password = await bcrypt.hash(user.password, 12);
return next();
} catch (err) {
return next(err);
}
});
Here is the solution thanks to Mahan for pointing it out.
result returns a Mongoose object so needs turning into a normal Javascript object first.
try {
let result = await newUser.save();
result = result.toObject();
delete result.password;
res.json(result);
} catch (err) {
res.json(err.errors);
}

Correct way to figure out what rejection a promise had?

I have an API / express router:
router.post("/signup", async function (req, res) {
try {
var user = await controllers.user.register(req.body.username, req.body.password);
req.session.user = user;
res.json(user);
} catch (e) {
res.status(500).json("DB Error");
}
});
Currently, on error, it returns 500 DB error. This is my controller:
function register(username, password) {
return new Promise((resolve, reject) => {
User.findOne({ username: username }).lean().exec((e, doc) => {
if (e) reject(e);
if (doc) {
reject("Username already exists.");
} else {
var user = new User({ username, password: hash(password) });
user.save((e) => {
if (e) reject(e);
else {
delete user.password;
resolve(user);
}
});
}
});
});
}
What's the right way to return a 400 if username already exists, and a 500 if it was a database error?
Mongoose already uses promises, the use of new Promise is promise construction antipattern.
Express doesn't have the concept of controllers, there are only route handlers and middlewares. Since register should be very aware of the way it will be used in a response, there may be no need for another level of abstraction above route handler. There will be no problem when a function has access to handler parameters and can form a response in-place.
It can be:
router.post("/signup", async function (req, res) {
try {
const { body, password } = req.body;
const user = await User.findOne({ username: username }).lean();
if (user) {
res.status(400).json("Username already exists");
} else {
...
res.json(user);
}
} catch (e) {
res.status(500).json("DB Error");
}
});
In case route handler needs to be reused in multiple places with some variations, it could be refactored to higher-order function or some other helper that is aware of original req and res parameters.
You can change the way you are rejecting the Promise. I'd suggest something like:
function register(username, password) {
return new Promise((resolve, reject) => {
User.findOne({ username: username }).lean().exec((e, doc) => {
if (e) reject(500);
if (doc) {
reject(400);
} else {
var user = new User({ username, password: hash(password) });
user.save((e) => {
if (e) reject(500);
else {
delete user.password;
resolve(user);
}
});
}
});
});
}
And in the route:
router.post("/signup", async function (req, res) {
try {
var user = await controllers.user.register(req.body.username, req.body.password);
req.session.user = user;
res.json(user);
} catch (e) {
res.status(e).json(e == 400 ? "Username already exists." : "DB Error");
}
});

how to chain statements together using promises

I'm new to node, and learning all about promises, and pg-promise specifically. This is what I want to do using Express and pg-promise:
check email,
if not found check username,
if not found create a new user.
return user id
I've got my repo set up (db.users) which does the sql which is working great.
In my authorization handler I'm not sure how to make the repo calls follow one another. The syntax seems clunky to me. Here's what I have so far:
exports.signup = function( req, res, next ) {
const username = req.body.username;
const email = req.body.email;
const password = req.body.password;
// See if a user with the given email exists
db.users.byEmail({email: email})
.then(user => {
if (user) {
return res.status(422).send({ error: 'Email is in use'});
} else {
return null; <-- must I return something here?
}
})
.then(() => {
db.users.getUsername({username: username})
.then(user => {
if (user) {
return res.status(422).send({ error: 'Email is in use'});
} else {
return null; <-- must I return something here?
}
...etc
})
Maybe pg-promises don't chain together like this? Should they be nested within one another or maybe be completely separate blocks? Also, not quite sure where the catch goes. I've tried every way I can think of, following various tutorials, but I get errors like 'can't set headers that already set' and 'promises aren't being returned'. If any kind person can guide me here, I'd really appreciate it.
You must use a task when executing multiple queries, so they can share one connection, or else the connection management will suffer from performance issues.
db.task(t => {
return t.users.byEmail({email})
.then(user => {
return user || t.users.getUsername({username});
});
})
.then(user => {
if (user) {
res.status(422).send({error: 'Email is in use'});
} else {
// do something else
}
})
.catch(error => {
// process the error
});
It will work well, presuming that your repositories were set up as shown in pg-promise-demo.
The previous 2 answers were really bad advise, and completely against what pg-promise says you should do. See Tasks versus root/direct queries.
And when doing multiple changes, you would usually use a transaction - use tx instead of task.
With guidance from vitaly-t I altered his answer to include separate error messages depending on which thing failed. I also moved up the next "then" into the transaction block to use the "t" of the transaction for the next step which creates user. Much thanks for vitaly-t!
db.task(t => {
return t.users.byEmail({email})
.then(user => {
if (user) {
throw new Error('Email is in use');
} else {
return t.users.byUsername({username});
}
})
.then((user) => {
if (user) {
throw new Error('Username is taken');
} else {
return t.users.addNew({username, email, password});
}
})
})
.then(user => {
res.json({token: tokenForUser(user), username: user.username, aCheck: user.is_admin});
})
.catch(error => {
res.status(422).json({ 'error': error.message});
});
Checking username and email existence is independent of each other. I think Promise.all() will suit your need more than sequentially chaining promise.
exports.signup = function (req, res, next) {
const username = req.body.username;
const email = req.body.email;
const password = req.body.password;
Promise.all([
db.users.byEmail({
email: email
}),
db.users.getUsername({
username: username
})
]).then((results)=> {
if (results[0] || results[1]) {
return res.status(422).send({
error: 'Email is in use' // same error msg as per your snippet
});
}
// here, code for creating a new user
});
};

Resources