await not working on mongoose model .findOne({email : req.body.email}) - javascript

I am new to express and mongodb... I am trying to use user.findOne({email}) function returns the user with email with await keyword but the node js throws error saying
let user = await User.findOne({ email: req.body.email });
^^^^^
SyntaxError: await is only valid in async function
const express = require('express');
const User = require("../models/user");
const {body, validationResult} = require("express-validator");
const router = express.Router();
// Listening the request at assigned route
router.post('/', [
// Validation array
body("name", "Enter name").isLength({ min: 5 }),
body("email", "Enter valid email").isEmail(),
body("password", "password must not be less than 5").isLength({ min: 5 })
] ,
(req, res) => {
// Errors in a single json...
const errors = validationResult(req)
// If there error return the error json...
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
// Check if there any user with same email...
let user = await User.findOne({ email: req.body.email });
if (user) {
return res.status(400).json({ error: "A user with this email already exist..."});
}
// Create user...
// user = await User.create({
// name : req.body.name,
// email : req.body.email,
// password : req.body.password
// })
// res.json(user)
res.send("Successful request...👍");
})
module.exports = router

You can use await only in async functions.
You can then just write:
(req, res) => {
like
async (req, res) => {.
The other way would be to not use async (but that'd work too), is to use a callback function:
User.findOne({ email: req.body.email }).then((user)=>{ /* Your logic here */})
In general, I prefer to not mix up async/callbacks, so I stick to only use async or only use callbacks :)

Make the callback function asynchronous by using keyword
async (req, res) => {}

two problems are here:
if you want to use await your callback should be async:
async () => { await ... }
user in your code is referring to promises not use value came from promise. to do so you need to execute the query:
let user = await User.findOne({ email: req.body.email }).exec();
it is also better to use try catch to avoid your application breaks on bad request.
all in all:
async (req, res) => { // missing async
const errors = validationResult(req)
if (!errors.isEmpty()) {
res.status(400).json({ errors: errors.array() }); // no need for return
}
try {
let user = await User.findOne({ email: req.body.email}).exec(); // missing exec()
if (user) {
res.status(400).json({ error: "A user with this email already exist..."});
}
res.send("Successful request...👍");
}
catch (error) { console.log(error) }
})

Related

NodeJS: function not returning res.json

So I am making a post api for registration/signup. If a user is successfully been registered, an access token will be provided to the user for saving it in frontend.
Everything works, the username, password is saving in database along with the token. But the access token is not returning. I have used mongoDB as my database and used mongoose. Here what I have done so far:
Edited code
const UserModel = require("../models/userModel");
var bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const registration = async (req, res) => {
try {
const { email, password } = req.body;
if (!(email && password)) {
res.status(400).send("All input is required");
}
const existingEmail = await UserModel.find({ email: email });
if (existingEmail.length === 0) {
const userToken = jwt.sign({ email: email }, process.env.SECRET, {
expiresIn: "90d",
});
let hashedPassword = await bcrypt.hash(password, 8);
const user = await UserModel.create({
email,
password: hashedPassword,
token: userToken,
});
await userRegistration.save(function (err, result) {
if (err) {
console.error(err);
} else {
console.log(result);
}
});
res.json(userToken);
} else {
res.json("email has already been registered");
}
} catch (err) {
res.json(err);
}
};
module.exports = registration;
if I test the api in thunder client on vscode, it is returning {}, an empty object. Please tell me what I have done wrong?
const existingEmail = await UserModel.find({ email }); This line of yours will provide you the array of all the users because email property has nothing, it will be just like .find({})
If you are checking if the email inserted by user is already in your database or not, I suggest you do it like this: const existingEmail = await UserModel.find({ email : email});
This will return the document with email property's value equal to the email you received in req.body i.e. email : xyz#gmail.com
And In this line const userToken = jwt.sign({ email }, process.env.SECRET, {expiresIn: "90d",});
You are again making same mistake. The object you pass in payload, has email property, but no value/email is assigned to that property.
It's just like email : undefined
Here, do it like this jwt.sign({email : email}, process.env.SECRET, {expiresIn: '90d')})
So, I made a simple mistake in the code. I was trying to save userRegistration which was not defined. That's why the bug was occurring. I should be more careful about this.
Apart from what has been mentioned in the other answers, to me it looks like you are not giving the token to res.json anywhere.
Your function is returning the token, but I dont think its going anywhere. You need to pass the token to res.json, not return from the function.
You are using await as well as .then() which looks wrong to me. You have to use just one of them.
Update:
jwt.sign returns a string so userToken contains string. You are giving that string to res.json which is expecting a json. You need to pass an object to it.
Kindly try the below mentioned code.
const UserModel = require("../models/userModel");
var bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const registration = async (req, res) => {
try {
const { email, password } = req.body;
if (!(email && password)) {
res.status(400).send("All input is required");
}
const existingEmail = await UserModel.find({ email });
if (existingEmail.length === 0) {
const userToken = jwt.sign({ email }, process.env.SECRET, {
expiresIn: "90d",
});
let hashedPassword = await bcrypt.hash(password, 8);
const user = await UserModel.create({
email,
password: hashedPassword,
token: userToken,
});
const userRegistrationResponse = await userRegistration.save();
const responseObj = {
...userRegistrationResponse,
accesstoken: `${userToken}`
};
res.json(responseObj);
} else {
res.json("email has already been registered");
}
} catch (err) {
res.json(err);
}
};
module.exports = registration;

Cannot read properties of undefined (reading 'id')

I have a auth.js file And a middleware named as fetchuser code given beolow
Can anyone please tell me why am i getting this error.
I am using express js and mongoose but this error is occured during sending token to the user and verify the user whether is user logged in or not.
auth.js
const express = require('express');
const User = require('../models/User');
const router = express.Router();
const { body, validationResult } = require('express-validator');
const bcrypt = require('bcryptjs'); // it is used for password hashing
const jwt = require('jsonwebtoken');
const fetchuser=require('../middleware/fetchuser');
// Route:1 - Create a User using :POST. "/api/auth/createuser". NO Login Required.
router.post('/createuser', [
body('email', 'Enter valid email').isEmail(),
body('name', 'Enter valid email').isLength({ min: 3 }),
body('password').isLength({ min: 5 })
], async (req, res) => {
// Check fo vaidation whether is any rule(defined in User model) breaked or not
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
// Check Whether user with same email id exist or not
try {
let user = await User.findOne({ email: req.body.email });
if (user) {
return res.status(400).json({ error: "Sorry user with same email id already exist" });
}
// hashing of password
const salt = await bcrypt.genSalt(10);
const securePassword = await bcrypt.hash(req.body.password, salt);
// create A new User
user = await User.create({
name: req.body.name,
email: req.body.email,
password: securePassword
})
// returning user id in Token
const JWT_secret = "Rishiisa#boy";
const data = { user:{id: user.id} };
const auth_token = jwt.sign(data, JWT_secret);
res.json({ auth_token });
}
catch (error) {
console.error(error.message);
res.status(500).send("Internal server error");
}
})
// Route:2 - Login a User using credential. "/api/auth/login". NO Login Required.
router.post('/login', [
body('email', 'Enter valid email').isEmail(),
body('password', 'password can not be blank').exists(),
], async (req, res) => {
// Check for vaidation according to the rule defined at line no. 53, 54;
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
// destructure the email and password from body request
const { email, password } = req.body;
try {
// Checking whether email is exist or not
let user = await User.findOne({ email });
if (!user) {
return res.status(400).json({ error: "Please try to login using correct credentials" });
}
// Now Comparing password with help of bcryptjs
const comparepassword = await bcrypt.compare(password, user.password);
if (!comparepassword) {
return res.status(400).json({ error: "Please try to login using correct credentials" });
}
// Now if user enter coorect password and login then user got logged in;
// And We will send authtoken to user;
// returning user id in Token
const JWT_secret = "Rishiisa#boy";
const data = { user:{id: user.id} };
const auth_token = jwt.sign(data, JWT_secret);
res.json({ auth_token });
}
catch (error) {
console.error(error.message);
res.status(500).send("Internal server error");
}
})
// Route:3 - Get Loggedin User details using:POST "/api/auth/getuser" Login required
router.post('/getuser', fetchuser, async (req, res) => {
try {
const userid = req.user.id;
const user = await User.findById(userid).select("-password");
res.send(user);
} catch (error) {
console.error(error.message);
res.status(500).send("Internal server error");
}
})
module.exports = router
middleware:
fetchuser.js
const jwt = require('jsonwebtoken');
const JWT_secret = "Rishiisa#boy";
const fetchuser = (req, res, next) => {
// Get the user from jwt token and add user id to req object
const token = req.header('auth_token');
if (!token) {
res.status(401).send({ error: "Please authenticate using a valid token" });
}
try {
const data = jwt.verify(token, JWT_secret);
req.user = data.user;
next();
} catch (error) {
res.status(401).send({ error: "Please authenticate using a valid token" });
}
}
module.exports = fetchuser;
In auth.js, where you wrote: "const data = { user:{id: user.id} };" Try changing user.id to user._id, since in MongoDB the user id is referred to as '_id'.
Let me know if that works.
I've had problems sending jwt token back and even verifying it, but all is good on my side now.
Also, below is my (inspired) method of going about this:
router.post('/register', (req, res)=>{
const { username, password } = req.body;
const user = new User({
username,
password
});
bcrypt.genSalt(10, (err, salt)=>{
bcrypt.hash(user.password, salt, (err, hash)=>{
if(err) throw err;
user.password = hash;
user.save()
.then(user=>{
jwt.sign(
{ id: user._id },
process.env.jwtSecret,
{ expiresIn: 3600 },
(err, token) =>{
if(err) throw err;
res.status(200)
}
)
})
})
})
})

Error: Cannot set headers after they are sent back to the client in express middleware

I am having issues while doing some complex stuff inside middleware. I know i should be getting this done in a route or some small function but I had no choice.
This is the entire middleware:
app.use(async function addReqUser(req,res,next) {
if (req.cookies) {
let token = req.cookies['session-token'];
let user = {};
async function verify() {
//google auth function
const ticket = await client.verifyIdToken({
idToken: token,
audience: CLIENT_ID,
});
const payload = ticket.getPayload();
user.name = payload.name;
user.email = payload.email;
user.picture = payload.picture;
}
verify()
.then(async ()=>{
req.user = user;
const user = await User.find({ email: req.user.email }).select("_id").exec();
req.user.id = user[0]._id
res.locals.user = req.user //populate the user with an id
next();
})
.catch(err=>{
console.log(err)
return res.redirect('/')
})
}
next()
})
The error just says [ERR HTTP_HEADERS_SENT] Cannot set headers after they are sent back to the client
Help me with this.
Your fast response in appreciated.
If req.cookies is set and the verify() call is successful, next() will be executed twice: once after the asynchronous invocation of verify() and once more at the end of the .then function.
On the other hand, if verify() fails, the res.redirect call in the .catch function sets a header after the first execution of next().
Both can lead to the observed error. A middleware should either
optionally set headers and then call next() (once!) or
optionally set headers and then send a response (e.g., via res.send), but not call next().
You have not used async and await for verify function, you can modify you app.use to the following code
app.use(async function addReqUser(req, res, next) {
if (req.cookies) {
let token = req.cookies['session-token'];
let user;
async function verify() {
//google auth function
return client.verifyIdToken({
idToken: token,
audience: CLIENT_ID,
});
}
const response = await verify()
const payload = response.getPayload()
if (payload.email) {
user.name = payload.name;
user.email = payload.email;
user.picture = payload.picture;
const userDoc = await User.find({ email: req.user.email }).select("_id").exec();
req.user = userDoc;
req.user.id = userDoc[0]._id;
res.locals.user = req.user //populate the user with an id
return next()
} else {
return res.redirect('/')
}
}
next()
})
While using the .then on verify, the code is not waiting for verify() Promise to complete & next() after the if block is executed first
The req.cookies was being called and the next() was being executed twice. After some suggestions and coding here's the solution:
app.use(function addReqUser(req, res, next) {
if (Object.hasOwnProperty.bind(req.cookies)('session-token')) { //has a session-token
let user = {};
async function verify() {
let token = req.cookies['session-token'];
//google auth function
const ticket = await client.verifyIdToken({
idToken: token,
audience: CLIENT_ID,
});
const payload = ticket.getPayload()
if (payload) {
user.name = payload.name;
user.email = payload.email;
user.picture = payload.picture;
req.user = user;
const userDoc = await User.find({ email: req.user.email }).select("_id").exec();
req.user.id = userDoc[0]._id;
res.locals.user = req.user //populate the user with an id
}
}
verify()
.then(()=>{
next();
})
.catch(err=>{
console.log(err)
res.redirect('/')
})
} else {
next();
}
})

`SyntaxError: await is only valid in async function` while trying to register a user in node rest api

I'm trying to create a register route in my NodeJS rest api, but whenever I try to send the data to the db, it's showing me some error like SyntaxError: await is only valid in async function.
I know what's the meaning of the error but, don't know why this error is coming here.
Here's my register route code:
router.post("/register", async (req, res) => {
const firstName = req.body.firstName;
const lastName = req.body.lastName;
const email = req.body.email;
const phone = req.body.phone;
const gender = req.body.gender;
const country = req.body.country;
const password = req.body.passwordHash;
bcrypt.hash(password, 10, (err, hash) => {
if (err) {
console.log("error: " + err);
} else {
const newUser = new User({
firstName,
lastName,
email,
phone,
gender,
country,
passwordHash: hash,
});
try {
const savedUser = await newUser.save()
res.send(savedUser)
} catch (err) {
res.status(400).send(err)
}
}
});
});
Can someone please help me?
await is inside the (err, hash) => function. So you need async (err, hash) =>, not async (req, res) =>.

Trying to make a PUT request in node.js

I am trying to make a PUT request with node.js using Javascript. Basically, what I am trying to do is make it so that an authenticated user is allowed to update a phone number and password. Normally I would have just used req.body in order to have the body be used to make an update request, however the whole body has a username, password and phoneNumber. I am only needing to update the password and phoneNumber. I have a restrict function that is restricting this request except for a logged in registered user, and I also have a model function for my update which is:
function updateUser(changes, id) {
return db("users")
.update(changes)
.where({id})
}
I also am trying to make sure that the password the user decided to update to (or the password they currently have) is hashed. I am using bcryptjs to hash the password. I have a two post request that both encrypts the password, (which is my register function) and one that compares the encryption (my login function). I will include those both just in case you need any background information:
router.post("/register", async (req, res, next) => {
try {
const {username, password, phoneNumber} = req.body
const user = await Users.findBy({username}).first()
if(user) {
return res.status(409).json({
message: "Username is already in use",
})
}
const newUser = await Users.create({
username,
password: await bcrypt.hash(password, 14),
phoneNumber,
})
res.status(201).json(newUser)
} catch (err) {
next(err)
}
})
router.post("/login", async(req, res, next) => {
try {
const {username, password} = req.body
const user = await Users.findBy({username}).first()
if(!user) {
return res.status(401).json({message: "Invalid Username or Password",})
}
const passwordValid = await bcrypt.compare(password, user.password)
if(!passwordValid) {
return res.status(401).json({message: "Invalid Username or Password",})
}
const token = jwt.sign({
userId: user.id,
}, process.env.JWT_SECRET)
res.cookie("token", token)
res.json({
message: `Welcome to your plant page ${user.username}!`
})
} catch (err) {
next(err)
}
});
When I was trying to start my PUT request, I had started off writing const {phoneNumber, password} = req.body but I am needing to use both phoneNumber and password in the function. Here is an example of what I was starting my code with:
router.put("/:id/updateaccount", restrict(), async(req, res, next) => {
try {
const {phoneNumber, password} = req.body
} catch(err) {
next(err)
}
})
I got it figured out after finding some help from someone in my class. I was on the right track with const {phoneNumber, password} = req.body. The rest is this (or this is all of the code):
router.put("/:id/updateaccount", restrict(), async(req, res, next) => {
try {
const {phoneNumber, password} = req.body
const userUpdate = await Users.updateUser({
phoneNumber, password: await bcrypt.hash(password, 14)
}, req.params.id)
res.status(200).json({
userUpdate:userUpdate, message: "You have successfully updated your information",
})
} catch(err) {
next(err)
}
})
I again used bcrypt to encrypt the newly updated password

Categories

Resources