I am getting this error : Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client - mongodb

When I am sending POST Request for Login, then show this Error. I have used mongoose & MongoDB Atlas.
If I send POST request with valid email & password, it also shows this error.
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent
to the client
But POST request for registration is working well.
User Model
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const userSchema = new Schema({
name: {
type: String,
required: true,
trim: true
},
email: {
type: String,
required: true,
},
password: {
type: String,
required: true
},
balance: Number,
income: Number,
expense: Number,
transactions: {
type: [{
type: Schema.Types.ObjectId,
ref: 'Transaction'
}]
}
})
const User = mongoose.model('User', userSchema)
module.exports = User
User Controller
const registorValidate = require('../validator/registrationValidate')
const User = require('../models/userModel')
const bcrypt = require('bcrypt')
const loginValidate = require('../validator/loginValidator')
const jwt = require('jsonwebtoken')
module.exports = {
login: (req, res) => {
const { email, password } = req.body
let logValidate = loginValidate({ email, password })
if (!logValidate.isValid) {
res.status(400).json(logValidate.error)
return
}
User.findOne({ email })
.then(user => {
if (!user) {
console.log(`${email} not found`)
res.json({
msg: `${email} not found`
})
}
bcrypt.compare(password, user.password, (err, result) => {
if (err) {
res.status(400).json({
msg: 'Error occured'
})
}
if (!result) {
res.status(404).json({
msg: `Password doesn't match`
})
}
let token = jwt.sign({
_id: user._id,
name: user.name,
email: user.email
}, 'SECRET', { expiresIn: '2h' })
res.status(200).json({
msg: 'Login successful',
token: `Bearer ${token}`
})
})
return
})
.catch(err => {
res.status(500).json({
msg: 'Error occured'
})
})
res.end()
},
registration: (req, res) => {
let { name, email, password, confirmPassword } = req.body
let validate = registorValidate({ name, email, password, confirmPassword })
if (!validate.isValid) {
res.status(400).json(validate.error)
} else {
User.findOne({ email })
.then(user => {
if (user) {
res.json({
msg: `${email} is already exist`
})
} else {
bcrypt.hash(password, 11, (err, hash) => {
if (err) {
res.status(500).json({
msg: 'Server error occured'
})
}
let user = new User({
name,
email,
password: hash
})
user.save()
.then(user => {
res.status(201).json({
msg: `Thanks ${name} for your registration`,
user
})
})
.catch(err => {
res.status(500).json({
msg: 'Error occured'
})
})
})
}
})
.catch(err => {
res.status(500).json({
msg: 'Error occured'
})
})
}
}
}
Login Validator
const validator = require('validator')
const validate = user => {
let error = {}
// Email validator
if (!user.email) {
error.email = 'Please provide an Email'
} else if (!validator.isEmail(user.email)) {
error.email = 'Please provide a valid Email'
}
// Password validate
if (!user.password) {
error.password = 'Please provide a password'
} else if (user.password.length < 6) {
error.password = 'Password Must be greater or Equal to 6 characters'
}
return {
error,
isValid: Object.keys(error).length === 0
}
}
module.exports = validate
Thanks.

You don't need to put res.end() because when you called res.json() earlier, it already sent the response.
Please be advised that you should return when you call res.end(), res.send(), 'res.json()' and other operations that send the response, just like what you did with res.status(400).json(logValidate.error)
This should be one of the ways prevent you from getting ERR_HTTP_HEADERS_SENT, but keep in mind that if you have nested callbacks, you should return from the outer scope as well

Related

How change name of parameter on express response

I'm using mongoose and I would like that when I get all users send me uid instead of _id.
const allUssers = (req, res, next) => {
try {
User.find({})
.select("username")
.select("email")
.select("image")
.exec((err, users) => {
if (err) {
return res.status(400).json({
ok: false,
msg: "Error listing users",
});
}
return res.status(200).json({
ok: true,
users: users,
});
});
} catch (err) {
return res.status(500).json({
ok: false,
msg: "Please contact with administrator",
});
}
};
You can update your schema to use an alias:
let User = new Schema({
_id: { type: String, alias: "uid" }
});
Or you can map your users to something different:
return res.status(200).json({
ok: true,
users: users.map(({ _id, ...user }) => ({ uid: _id, ...user }),
});

How to get signed in users data?

I have a MERN mobile app thats using passportjs to authenticate and login users (with mongodb database and axios), however, when i eventually get to the the screen to enter in data (a "log"), i cant associate that data/log with the signed in user. How can i grab the user id several screens later after they have already signed in to associate it with the entry? My mongodb database has a number of users, so i only want a specific user's data (eg calories), ie the one that is currently logged in:
// Mongoose schemas
// log.model.js
const Schema = mongoose.Schema;
const logSchema = new Schema(
{
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
calories: {
type: Number,
required: true,
},
},
{
timestamps: true,
}
);
const Log = mongoose.model("Log", logSchema);
// user.model.js
const userSchema = new Schema(
{
_id: Schema.Types.ObjectId, // user id
email: {
type: String,
required: true,
unique: true,
trim: true,
},
password: {
type: String,
required: true,
trim: true,
minlength: 6,
},
},
{
timestamps: true,
}
);
const User = mongoose.model("User", userSchema);
They are first prompted to signin in the app, where they will then navigate to Home. Not all features are added in yet, just in development stage now:
// ./frontend/screens/signin.js
function onLoginPress() {
axios({
method: "POST",
data: {
email: email,
password: password,
},
withCredentials: true,
url: 'http:localhost:5000/users/signin',
})
.then((res) => console.log(res.data))
.catch((error) =>
console.log("ERROR: Promise rejected (sign in): " + error)
);
navigation.navigate("Home");
}
// ./backend/routes/users.js
router.route("/signin").post((req, res, next) => {
passport.authenticate("local", (error, user, info) => {
if (error) {
res.json({
status: "FAILED",
message: error,
});
}
if (!user) {
res.json({
status: "FAILED",
message: "No user exists",
});
} else {
req.logIn(user, (error) => {
if (error) console.log("ERROR: " + error);
res.json({
status: "SUCCESS",
message: "Successfully authenticated",
});
console.log(req.user);
});
}
})(req, res, next);
});
After they sign in, and they wish to enter in calories, i attempt to associate that log (and any future logs they might add) with the signed in user when they hit a button:
// ./frontend/screens/log.js
const [calories, setCalories] = React.useState("");
function onSaveLog() {
axios({
method: "post",
url: "http://localhost:5000/log/add",
data: {
calories: calories,
// CANT GET USER ID HERE?
},
})
.then((res) => {
console.log(res.data);
})
.catch(function () {
console.log("LOG ERROR: promise rejected");
});
}
// ./backend/routes/log.js
router.route("/add").post((req, res) => {
const calories = Number(req.body.calories);
// const user = req.body.user; // CANT GET THE USER ID HERE
const newLog = new Log({
calories,
// user,
});
// saves Log data to mongodb
newLog
.save()
.then(() => res.json("Log added"))
.catch((err) => res.status(400).json("Error: " + err));
});
so, what you doubt is, correct me if I'm wrong is that you want an ID that can be accessed somewhere later in the app to retrieve the users' data.
There are many ways to achieve that,
after you get the id, you can pass it as Navparams. check this for more info RN- params
Next you can store the id in async storage and retrieve it anywhere, I would suggest this cause is the easiest rn--async storage
import AsyncStorage from '#react-native-async-storage/async-storage';
const storeData = async (value) => {
try {
await AsyncStorage.setItem('#storage_Key', value)
} catch (e) {
// saving error
}
}
// read
const getData = async () => {
try {
const value = await AsyncStorage.getItem('#storage_Key')
if(value !== null) {
// value previously stored
}
} catch(e) {
// error reading value
}
}
you can do it this way, do tell me if you're stuck

I can't update a users info to add a password reset link

I'm having trouble getting a variable within a database user to update using lodash. It seems to update in one route (password in /resetpassword) but not in the other. (resetLink in /forgotpassword)
I need to have "resetLink" update to the new token generated by JWT, in order to create a link to reset the users password.
/forgotpassword
router.put('/forgotpassword', (req, res) => {
const { email } = req.body;
if (!email) {
return res.status(400).json({ msg: "Please enter all fields" });
} else {
// Find existing User by email address
User.findOne({ email }).then((user) => {
if (!user) return res.status(400).json({ msg: "That email doesn't exist with us..." });
const payload = {
id: user._id,
}
const secret = process.env.JWT_SECRET;
const token = jwt.sign(payload, secret, {
expiresIn: '15m'
});
const obj = {
resetLink: token
}
console.log("obj is " + JSON.stringify(obj));
console.log("User is " + user);
user = _.extend(user, obj);
user.save((err, result) => {
if (err) {
return res.status(400).json({{ error: "Something went wrong..." + err }});
} else {
return res.status(200).json({ msg: "Success! These should match: TOKEN - " + token + " / RESET LINK - " + user.resetLink })
}
})
});
};
});
/resetpassword
router.put('/resetpassword', (req, res) => {
const { resetLink, newPass } = req.body;
if (resetLink) {
jwt.verify(resetLink, process.env.JWT_SECRET, function (err, decodedData) {
if (err) {
return res.status(401).json({ msg: "Incorrect token or it is expired." });
}
User.findOne({ resetLink }, (err, user) => {
if (err || !user) {
return res.status(400).json({ error: "User with this token doesn't exist" });
}
// newPass will be hashed by jwt eventually
const obj = {
password: newPass
}
user = _.extend(user, obj);
user.save((err, result) => {
if (err) {
return res.status(400).json({ error: "Reset password error" });
} else {
return res.status(200).json({ message: "Your password has been changed" });
}
})
})
})
} else {
return res.status(401).json({ error: "Authentication error!" });
}
});
Mongoose User model
const UserSchema = new Schema({
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
lowercase: true,
},
password: {
type: String,
required: true,
},
register_date: {
type: Date,
default: Date.now,
},
resetLink: {
data: String,
default: '',
}
});
As always I appreciate any help at all. Thanks.
EDIT: Here is the returned error -
"error": "Something went wrong...ValidationError: resetLink: Cast to Object failed for value \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVmM2JjN2MwNGVmMmRkNGE2ODkwZDEwYiIsImlhdCI6MTU5Nzc1NDY2MCwiZXhwIjoxNTk3NzU1NTYwfQ.BL8yYsqk2A5hGlNTPa2AxtD_iJ1lWELiCgtpcCkFB6I\" at path \"resetLink\""
My Schema was off. Apologies.
resetLink: {
data: String,
default: '',
}
Should be:
resetLink: {
Type: String,
default: '',
}

How to read registered users list from mean stack server

I would like to be able to read the registered users list from the Mean Stack Mongo DB server and display it on my Web App.
So far, I've only been able to access this list, which is encrypted, through the terminal by running the Mongo shell.
Any ideas? This is the code piece where "getUsers" should be implemented, but my "getUsers" attempts have not been successful so far.
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const User = require("../models/user");
exports.createUser = (req, res, next) => {
bcrypt.hash(req.body.password, 10).then(hash => {
const user = new User({
email: req.body.email,
password: hash
});
if(req.body.adminCode === "secretCode123") {
newUser.isAdmin === true;
}
user
.save()
.then(result => {
res.status(201).json({
message: "User created!",
result: result
});
})
.catch(err => {
res.status(500).json({
message: "Invalid authentication credentials!"
});
});
});
};
// Here we create the token
exports.userLogin = (req, res, next) => {
let fetchedUser; // Otherwise user would only exist in the first then block
User.findOne({ email: req.body.email })
.then(user => {
if (!user) {
return res.status(401).json({
message: "Authentication failed"
});
}
fetchedUser = user;
console.log("The fetched user is " + fetchedUser);
console.log("The user is " + user);
console.log("The encrypted req.body.password is " + req.body.password);
console.log("The user.password is " + user.password);
return bcrypt.compare(req.body.password, user.password);
})
.then(result => {
console.log("The result is " + result); // It is either false or true
if (!result) {
return res.status(401).json({
message: "Authentication failed" // "Return" prevents execution of next part of code
});
}
const token = jwt.sign(
{ email: fetchedUser.email, userId: fetchedUser._id },
process.env.JWT_KEY,
{ expiresIn: "1h" }
);
res.status(200).json({
token: token, // No need to return because no code afterwards
expiresIn: 3600,
userId: fetchedUser._id
});
console.log("The token is " + token)
})
.catch(err => {
return res.status(401).json({
message: "invalid authentication credentials!"
});
});
};
/* exports.getUsers = (req, res, next) => {
const listofUsers = [
{ id: 12, email: "Narco#test.com" },
{ id: 13, email: "Bombasto#test.com" },
{ id: 14, email: "Celeritas#test.com" },
{ id: 15, email: "Magneta#test.com" },
{ id: 16, email: "RubberMan#test.com" },
{ id: 17, email: "Dynama#test.com" },
{ id: 19, email: "Magma#test.com" },
{ id: 20, email: "Tornado#test.com" }
];
res.status(200).json({
message: "Dummy User ID fetched from the server",
admin: listofUsers
})
} */
exports.getUsers = (req, res, next) => {
const userQuery = User.find();
let fetchedUsers;
userQuery
.then(userDocuments => {
const token = jwt.sign(
{ email: fetchedUser.email, userId: fetchedUser._id },
process.env.JWT_KEY,
{ expiresIn: "1h" }
);
res.status(200).json({
token: token, // No need to return because no code afterwards
expiresIn: 3600,
userId: fetchedUser._id
});
console.log("The token is " + token)
fetchedUsers = userDocuments;
console.log("The fetchedUsers are: "+ fetchedUsers);
return User.count();
})
.then(count => {
res.status(200).json({
message: "Users fetched successfully!",
users: fetchedUsers,
maxUsers: count
});
console.log(fetchedUsers);
})
.catch(error => {
res.status(500).json({
message: "Fetching users failed!"
});
});
}
Thanks
GB

MongoDB & Mongoose: unable to populate a user's posts with .populate()

I've searched this site for days looking through the many different but similar questions on this topic to no avail.
Here's what I'd like to happen. A user signs in and their posts are automatically linked to the users collection. Eventually I'd like to link posts to the profile it was posted to, but i"m not quite there yet. Here's what I've tried so far.
In the User Schema:
const UserSchema = new Schema({
posts: [{
type: Schema.Types.ObjectId,
ref: 'posts'
}],
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
},
...
});
module.exports = User = mongoose.model('users', UserSchema);
In the Post Schema:
const PostSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'users'
},
text: {
type: String,
required: true
},
name: {
type: String
},
...
});
module.exports = Post = mongoose.model('posts', PostSchema);
In my users api, here's how I'm signing the user in and attempting to populate the user's posts:
const User = require('../../models/User');
router.post('/login', (req, res) => {
const { errors, isValid } = validateLoginInput(req.body);
// Check Validation
if (! isValid) {
return res.status(400).json(errors);
}
const email = req.body.email;
const password = req.body.password;
// Find user by email
User.findOne({ email })
.populate('posts')
.then(user => {
if (! user) {
errors.email = 'User not found';
return res.status(400).json(errors);
}
// Check password
bcrypt.compare(password, user.password).then(isMatch => {
if (isMatch) {
// User Matched
// Create JWT Payload
const payload = {
id: user.id,
firstName: user.firstName,
lastName: user.lastName,
name: user.firstName + ' ' + user.lastName,
avatar: user.avatar,
posts: user.posts
};
jwt.sign(
payload,
keys.secretOrKey,
{ expiresIn: 3600 }, (err, token) => {
res.json({
success: true,
token: 'Bearer ' + token,
payload
});
});
} else {
errors.password = 'Password is incorrect';
return res.status(400).json(errors);
}
});
});
});
In the posts api, here's how the post is being submitted:
router.post('/', passport.authenticate('jwt', { session: false }), (req, res) => {
const { errors, isValid } = validatePostInput(req.body);
if (! isValid) {
// Return errors with 400 status
return res.status(400).json(errors)
}
const newPost = new Post({
text: req.body.text,
name: req.body.name,
avatar: req.body.avatar,
user: req.user.id
});
newPost.save().then(post => res.json(post));
});
Currently, all I'm seeing is an empty array and no errors. I've been spinning my wheels on this one for a couple days now so any help would be appreciated. Thanks!
I think you forgot to save the _id of your new post to the User model so that the populate() can lookup the posts to populate:
newPost.save().then(post => {
User.update({ _id: req.user.id }, { $push: { posts: post._id }}, (err) => {
res.json(post));
});
});