at the moment I'm following a tutorial to push myself in the topics node + react.
The link to the repo is https://bitbucket.org/grtn/cloudstruct/src/develop/
When I make a post request to /api/users/register i get the following error in the console and i can't figure out why.
/Users/****/Dev/cloudstruct/routes/api/users.js:38
if(err) throw err;
^
Error: Illegal arguments: undefined, string
at _async (/Users/****/Dev/cloudstruct/node_modules/bcryptjs/dist/bcrypt.js:214:46)
at Object.bcrypt.hash (/Users/****/Dev/cloudstruct/node_modules/bcryptjs/dist/bcrypt.js:220:13)
at bcrypt.genSalt (/Users/****/Dev/cloudstruct/routes/api/users.js:37:28)
at Immediate.<anonymous> (/Users/****/Dev/cloudstruct/node_modules/bcryptjs/dist/bcrypt.js:153:21)
at runCallback (timers.js:756:18)
at tryOnImmediate (timers.js:717:5)
at processImmediate [as _immediateCallback] (timers.js:697:5)
[nodemon] app crashed - waiting for file changes before starting...
The Usermodel looks like this:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
// Create Schema
const UserSchema = new Schema({
name:{
type: String,
required: true
},
email:{
type: String,
required: true
},
password:{
type: String,
required: true
},
avatar:{
type: String
},
date:{
type: Date,
default: Date.now
}
});
module.exports = User = mongoose.model('users', UserSchema);
And my routing:
const express = require ('express');
const router = express.Router();
const gravatar = require('gravatar');
const bcrypt = require('bcryptjs');
// Load User model
const User = require ('../../models/User')
//#route GET api/users/test
//desc Tests post route
//#access Public
router.get('/test', (req, res) => res.json({msg: '<h1>Hello World</h1>'}));
//#route POST api/users/register
//desc Register User
//#access Public
router.post('/register', (req, res) =>{
User.findOne({ email: req.body.email })
.then(user => {
if(user) {
return res.status(400).json({email: 'Email already exists'});
} else {
const avatar = gravatar.url(req.body.email, {
s: '200',
r: 'pg',
d: 'mm'
});
const newUser = new User({
name: req.body.name,
email: req.body.email,
avatar: avatar,
password: req.body.password
});
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(newUser.password, salt, (err, hash) => {
if(err) throw err;
newUser.password = hash;
newUser
.save()
.then(user => res.json(user))
.catch(err => console.log(err));
})
});
}
});
});
module.exports = router;
Thanks for your help!
Your newUser.password is undefined. I am afraid that we can access mongoose docs like this. The solution in this case is to use req.body.password in .hash()
For reference: If you want to access the key value of a mongoose doc, your have to parse the doc into JSON.
The correct way to hash passwords with Mogoose is to use presave middleware. It will give you a guarantee that user password will always be hashed regardless of the place where you want to create a user record. Also, it's better for architecture: hashing will be made in the data layer instead of routing.
Here is a good example. Middleware is described in the step 2.
Related
I'm working on a personal project and i really need some help
I'm having this error and i don't understand why :(
Here is my code :
//authRoutes.js
const { Router } = require('express');
const authController = require('../controllers/authController');
const { requireAuth } = require('../middleware/authMiddleware');
var Post = require('../models/post')
const router = Router();
router.use(function(req,res, next){
res.locals.user = req.user;
next();
});
router.get('/signup', authController.signup_get);
router.post('/signup', authController.signup_post);
router.get('/login', authController.login_get);
router.post('/login', authController.login_post);
router.get('/logout', authController.logout_get);
router.get("/home", function (req, res) {
res.render("home");
});
router.get("/about", function (req, res) {
res.render("about");
});
router.get("/", requireAuth, function(req,res){
Post.find({userID:req.user._id}).exec(function(err, posts){
if(err){
console.log(err);
}
res.render("posts",{posts:posts})
})
})
router.get("/add", requireAuth, function(req,res){
res.render("addpost")
})
Everything was working fine until I tried to add a new post to the database
This is the part of the code that's causing the error :
router.post("/add",requireAuth, function(req,res){
var newPost = new Post({
title:req.body.title,
content:req.body.content,
userID:req.user._id
})
newPost.save(function(err,post){
if(err){
console.log(err)
res.redirect("/posts")
}
})
})
module.exports = router;
can someone help me?
The error is because in here:
var newPost = new Post({
title:req.body.title,
content:req.body.content,
userID:req.user._id
})
you're trying to access "user._id", but there is not "_id" inside user, so check what is inside user.
Quite literally, it is what it say it is
Cannot read properties of undefined (reading '_id')
This means that there's no _id property inside req.user (the object user inside the req object).
It seems you're not sending _id (maybe your payload is using id instead of _id? Could you share your payload with us?
it seems like req.user is undefined and that is the reason of why it gives you that error. Try to find out where you get your id from! :)
Here is user.js
const mongoose = require('mongoose');
const { isEmail } = require('validator');
const bcrypt = require('bcrypt');
const userSchema = new mongoose.Schema({
email: {
type: String,
required: [true, 'Please enter an email'],
unique: true,
lowercase: true,
validate: [isEmail, 'Please enter a valid email']
},
password: {
type: String,
required: [true, 'Please enter a password'],
minlength: [6, 'Minimum password length is 6 characters'],
}
});
// fire a function before doc saved to db
userSchema.pre('save', async function(next) {
const salt = await bcrypt.genSalt();
this.password = await bcrypt.hash(this.password, salt);
next();
});
// static method to login user
userSchema.statics.login = async function(email, password) {
const user = await this.findOne({ email });
if (user) {
const auth = await bcrypt.compare(password, user.password);
if (auth) {
return user;
}
throw Error('incorrect password');
}
throw Error('incorrect email');
};
const User = mongoose.model('user', userSchema);
module.exports = User;
So I know there are tons of similar questions out there, and I've read most of them in the past few days. However I didn't find any solution to my problem. The app is about users can post memories(cards) etc... Point is, when I create a new card with POST request, there is no problem, but when I want to sign up a user then all hell breaks loose and throws this error:
(node:2732) UnhandledPromiseRejectionWarning: TypeError: Cannot destructure property 'firstName' of 'req.body' as it is undefined.
at signup (file:///E:/projects/personal/memories-app/backend/controllers/user.controller.js:39:13)
at Layer.handle [as handle_request] (E:\projects\personal\memories-app\backend\node_modules\express\lib\router\layer.js:95:5)
at next (E:\projects\personal\memories-app\backend\node_modules\express\lib\router\route.js:137:13)
I don't know that could be the problem, because other functions work so dunno really.
Here are the codes
server.js
import express from 'express';
import mongoose from 'mongoose';
import cors from 'cors';
import dotenv from 'dotenv';
import postRoutes from './routes/posts.routes.js';
import userRoutes from './routes/users.routes.js';
const app = express();
dotenv.config();
app.use(express.json({ extended: true }));
app.use(express.urlencoded({ extended: true }));
app.use(cors());
app.use('/posts', postRoutes);
app.use('/users', userRoutes);
app.get('/', (req, res) => {
res.send('Hello to Memories API');
});
const PORT = process.env.PORT || 5000;
mongoose
.connect(process.env.CONNECTION_URL, {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() =>
app.listen(PORT, () => console.log(`Server running on port: ${PORT}`))
)
.catch((error) => console.log(error.message));
mongoose.set('useFindAndModify', false);
user.model.js
import mongoose from 'mongoose';
const userSchema = mongoose.Schema({
name: { type: String, required: true },
email: { type: String, required: true },
password: { type: String, required: true },
id: { type: String },
});
export default mongoose.model('User', userSchema);
the sign up method from user.controller.js
import bcrypt from 'bcryptjs';
import jwt from 'jsonwebtoken';
import User from '../models/user.js';
export const signup = async (res, req) => {
const { firstName, lastName, email, password, confirmPassword } = req.body;
try {
const existingUser = await User.findOne({ email });
if (existingUser)
return res.status(400).json({ message: 'User already exists' });
if (password !== confirmPassword)
return res.status(400).json({ message: "Passwords don't match" });
const hashedPassword = await bcrypt.hash(password, 12);
const result = await User.create({
email,
password: hashedPassword,
name: `${firstName} ${lastName}`,
});
const token = jwt.sign(
{ email: result.email, id: result._id },
'test',
{
expiresIn: '1h',
}
);
res.status(200).json({ result, token });
} catch (error) {
res.status(500).json({ message: 'Something went wrong.' });
}
};
and just to see the createPost method (which works) from post.controller.js
import PostMessage from '../models/postMessage.js';
import mongoose from 'mongoose';
export const createPost = async (req, res) => {
const post = req.body;
console.log(post);
const newPost = new PostMessage(post);
try {
await newPost.save();
res.status(201).json(newPost);
} catch (error) {
res.status(409).json({ message: error.message });
}
};
And there is no problem with the front-end because when I simply console.log the req, I can see the body, but if I were to clg the req.body, then it is undefined. I've tried it with postman also, but no luck.
I would appreciate any insight on this! Thanks in advance!
You need to swap the order of res and req in the signup function, replace:
export const signup = async (res, req) => {
by:
export const signup = async (req, res) => {
Your User model does not have a firstName, lastName, and confirmPassword types use { name, email, password, } = req.body to sign up a new user.
In your project frontend use
name email, password, confirmPassword to register a use and email, password to log users in.
I'm working with routes on node js. I created a user model shown below -
const mongoose = require("mongoose");
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
const validator = require("validator");
require("dotenv").config();
const userSchema = mongoose.Schema(
{
email: {
type: String,
required: true,
unique: true,
trim: true,
lowercase: true,
validate(value) {
if (!validator.isEmail) {
throw new Error("Invalid Email");
}
},
},
password: {
type: String,
required: true,
trim: true,
},
role: {
type: String,
enum: ["user", "admin"],
default: "user",
},
name: {
type: String,
required: true,
maxlength: 21,
},
phone: {
required: true,
type: Number,
maxlength: 12,
},
},
{ timestamps: true },
);
userSchema.pre("save", async function (next) {
if (user.isModified("password")) {
// hash the password
const salt = await bcrypt.genSalt(10);
const hash = await bcrypt.hash(this.password, salt);
this.password = hash;
}
next();
});
const User = mongoose.model("User", userSchema);
module.exports = {
User,
};
And then I created a file containing user routes shown below -
const express = require("express");
const router = express.Router();
require("dotenv").config();
const { User } = require("../../models/userModel");
router.route("/signup").post(async (req, res) => {
// const { email, password, name, phone } = req.body;
console.log(req.body);
// try {
// // Check if user email exists
// // create user instance and hash password
// const user = new User({
// email: req.body.email,
// password: req.body.password,
// name: req.body.name,
// phone: req.body.phone,
// });
// // generate jwt token
// console.log("user is saving");
// const userDoc = await user.save();
// // send email
// // save....send token with cookie
// res
// .cookie("access-token", "jflsakjusdilfjadslfj32j43lrf")
// .status(200)
// .send(userDoc);
// } catch (error) {
// res
// .status(400)
// .json({ message: "Error while creating user", error: error });
// }
const user = new User({
name: req.body.name,
email: req.body.email,
password: req.body.password,
phone: req.body.phone,
});
user
.save()
.then((doc) => {
console.log("User saved");
res.send(doc);
})
.catch((err) => {
console.log(err);
});
});
module.exports = router;
But don't know why I'm getting this error -
ReferenceError: user is not defined
at model.<anonymous> (D:\CovidHelpers\CovidHelpers\server\models\userModel.js:46:3)
at callMiddlewareFunction (D:\CovidHelpers\CovidHelpers\node_modules\kareem\index.js:483:23)
at model.next (D:\CovidHelpers\CovidHelpers\node_modules\kareem\index.js:58:7)
at _next (D:\CovidHelpers\CovidHelpers\node_modules\kareem\index.js:107:10)
at D:\CovidHelpers\CovidHelpers\node_modules\kareem\index.js:508:38
at processTicksAndRejections (internal/process/task_queues.js:75:11)
I have just created a new project in mongodb, gave database and network access and it's connecting successfully but also getting this error
I have done this before also and it was working fine but don't know why am I getting this now :(
Any help is appreciated
save is document middleware and in document middleware functions, this refers to the document. So in your case, I believe it should be this.isModified("password") instead of user.isModified("password").
You can delete userSchema.pre() middleware and transfer the password hashing logic inside the router. Also you can simplify your router code like this:
router.route("/signup").post(async (req, res) => {
try {
const salt = await bcrypt.genSalt(10);
const hash = await bcrypt.hash(req.body.password, salt);
req.body.password = hash;
let user = await User.create(req.body)
res.status(200).json(user)
} catch (error) {
res.status(400).json({ error: error });
}
});
RECOMMENDATION:
I would recommend you to try the great Mongoose plugin called passport-local-mongoose that will do this for you out of the box, and it will also give you some nice authentication features if you are using passport for authentication.
Package: https://www.npmjs.com/package/passport-local-mongoose
You don't actually get access to the document, in the mongoose's pre('save') hook.
For your usecase, you can do the hasing before you save the user.
This is my database connection:
app.js
const express = require("express");
const app = express();
var { MongoClient } = require("mongodb");
MongoClient.connect("mongodb://localhost:27017", (err, client) => {
if (err) return console.log(err);
db = client.db("MyDb");
app.listen(5000, () => {
console.log("listening on 5000");
});
});
And this is my insert function:
router.post(
"/register",
[
check("email")
.notEmpty()
.withMessage("Email Field is empty"),
check("email")
.isEmail()
.withMessage("Your email is not valid")
],
function(req, res) {
const errors = validationResult(req);
if (errors.length >= 0) {
res.render("register", { errors: errors.errors });
console.log(errors.errors);
return;
}
const { name, email, password } = req.body;
const newUser = new User({
name: name,
email: email,
password: password
});
newUser.save(function(err) {
if (err) throw err;
console.log(true);
});
}
);
And this is my user model:
User.js
const mongoose = require("mongoose");
const UserSchema = mongoose.Schema({
name: { type: String, require: true },
email: { type: String, require: true, unique: true },
password: { type: String, require: true },
created_at: Date,
updated_at: Date
});
const User = mongoose.model("User", UserSchema);
module.exports = User;
There is no error in terminal or browser. When I click the "register" button, the app will freeze and there is no error message at all.
I already tested many tips concerning the database connection but couldn't solve the issue.
I find there are two order of problems in the proposed code, at least as we can read it in your question:
First, I can't find any binding between mongoose and the established mongodb connection
Second, your route handler does not seem to return any status code / content to the
caller
So, for as I see it, you can
change connection setup as follows
mongoose.connect('mongodb://localhost/test', {useNewUrlParser: true})
.then((conn, err) => {
app.listen(5000, () => {
console.log("listening on 5000");
});
});
in order to bind mongoose with MongoDb configuration
retust a status code, e.g. 201, when the new User has been saved:
newUser.save(function(err) {
console.log('Result', err)
if (err) throw err;
console.log(true);
res.send(201)
});
This way I prevent the application hanging up on receiving request...
I hope this can help you!
validationResult() "Extracts the validation errors from a request and makes them available in a Result object." https://express-validator.github.io/docs/validation-result-api.html Therfore, if you don't have any errors this object will contain no errors ( you can check with .isEmpty()), your endpoint doesn't send a response, and leaves the requestor waiting.
this is my mean code.
const express = require('express');
const router = express.Router();
const passport = require('passport');
const jet = require('jsonwebtoken');
const Contact = require('../models/contacts');
// retrieving Data
router.get('/contacts',(req,res,next)=>{
// res.send('Retriving the contact list');
console.log('contacts page');
Contact.find(function(err, contacts){
res.json(contacts);
})
});
// to add the content
router.post('/contact',(req, res, next)=>{
// logic to add contact
let newContact = new Contact({
first_name: req.body.first_name,
last_name: req.body.last_name,
email_id: req.body.email_id,
password: req.body.password
});
Contact.addRegistry((err, contacts)=> {
if(err) {
res.json({msg:'faild to add register'});
}
else{
res.json({msg:'registry added sucessfully'});
}
});
});
// to delete the content
router.delete('/contact/:id',(req, res, next) =>{
// logic to delete contact
Contact.remove({_id:req.params.id}, function(err, result){
if(err){
res.json(err);
}
else {
res.json(result);
}
});
})
module.exports = router;
the above file is route.js.
the below code is from contact.js
// Database code.
var express = require('express');
var app = express();
var mongoose = require('mongoose');
var bcrypt = require('bcryptjs');
// database schaema
var ContactSchema = new mongoose.Schema({
first_name: String,
last_name: String,
id: String,
location: String,
profile_picture_url: String,
email_id: String,
phone: String,
job_title: String,
company: String,
education: String,
password: String,
savedjobslist: {
title: [],
subtitle: []
},
appliedjobslist: {
title: [],
subtitle: []
},
failedjobslist: {
title: [],
subtitle: []
}
});
const Contact = module.exports = mongoose.model('Contact', ContactSchema);
module.exports.getUserById = function(id,callback) {
Contact.findById(id,callback);
}
module.exports.getUserById = function(username,callback) {
const query = {username: username}
Contact.findOne(query,callback);
}
module.exports.addRegistry = function(newContact,callback) {
bcrypt.genSalt(10, (err,salt) => {
bcrypt.hash(newContact,salt, (err,hash) => {
if (err) {
console.log(err);
}
newContact.password = hash;
newContact.save(callback);
});
});
}
I'm trying to post the data from postman it is shoing the error as
"there was error connecting to http://localhost:3000/api/contact"
and in the command prompt it is showing the error as
Server started at port3000 connected to mongos database at 27017
Error: Illegal arguments: function, string
at _async (D:\project-1\back-end\node_modules\bcryptjs\dist\bcrypt.js:214:46 )
at Object.bcrypt.hash (D:\project-1\back-end\node_modules\bcryptjs\dist\bcry pt.js:220:13)
at bcrypt.genSalt (D:\project-1\back-end\models\contacts.js:49:16)
at Immediate._onImmediate (D:\project-1\back-end\node_modules\bcryptjs\dist\ bcrypt.js:153:21)
at runCallback (timers.js:794:20)
at tryOnImmediate (timers.js:752:5)
at processImmediate [as _immediateCallback] (timers.js:729:5) D:\project-1\back-end\models\contacts.js:54
newContact.save(callback);
^
TypeError: newContact.save is not a function
at bcrypt.hash (D:\project-1\back-end\models\contacts.js:54:23)
at runCallback (timers.js:794:20)
at tryOnImmediate (timers.js:752:5)
at processImmediate [as _immediateCallback] (timers.js:729:5) [nodemon] app crashed - waiting for file changes before starting...
newContact.save(callback);
^
TypeError: newContact.save is not a function.
i don't know why this error is coming.
You have an issue here:
bcrypt for generating is throwing an error because of wrong parameters. You can't pass object (newContact) to bcrypt.
Try to generate a hash using the following code:
const salt = bcrypt.genSaltSync(10);
const hashedPassword = bcrypt.hashSync(password, salt);
You can use pre save function of mangoose to generate hashedPassword while storing this. Personally, I don't prefer as this adds new check everytime you save the object
find() takes query as an argument to search all pass {}
Contact.find({},function(err, contacts){
res.json(contacts);
})
Contact.addRegistry waits newContact as first parameter, but you do not pass it on your route.js
I think you want to do something like this
ContactSchema.pre('save', function(next) {
const user = this;
// generate a salt
bcrypt.genSalt(10, function(err, salt) {
if (err) return next(err);
// hash your password
bcrypt.hash(user.password, salt, function(err, hash) {
if (err) return next(err);
// store hash on the password field
user.password = hash;
next();
});
});
});