When i trying to add new user by route via Postman, using syntax like:
{
"name":"Test Name",
"email":"testmail#gmai.com",
"username":"test123",
"password":"3214"
}
console response me an error: "Illegal arguments: undefined, string"
and postman says "could not get any response"
My code example (user.js):
const UserSchema = mongoose.Schema({
name: {
type: String
},
email: {
type: String,
required: true
},
username: {
type: String,
required: true
},
password: {
type: String,
required: true
}
});
const User = module.exports = mongoose.model('User', UserSchema);
module.exports.addUser = function(newUser, callback){
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(newUser.password, salt, (err, hash) => {
if(err) throw err;
newUser.password = hash;
newUser.save(callback);
});
});
};
api.js:
router.post('/register', (req, res, next) => {
let newUser = new User({
name: req.body.name,
email: req.body.email,
username: req.body.username,
password: req.body.password
});
User.addUser(newUser, (err, user) => {
if(err){
res.json({success: false, msg:'Failed to register user'});
} else {
res.json({success: true, msg:'User registered'});
}
});
});
I want to use bcrypt, how to fix it?
EDIT: whole error log:
C:\Users\ajaks\Desktop\meanproject\server\models\user.js:38
if(err) throw err;
^
Error: Illegal arguments: undefined, string
at _async (C:\Users\ajaks\Desktop\meanproject\node_modules\bcryptjs\dist\bcrypt.js:214:46)
at Object.bcrypt.hash (C:\Users\ajaks\Desktop\meanproject\node_modules\bcryptjs\dist\bcrypt.js:220:13)
at bcrypt.genSalt (C:\Users\ajaks\Desktop\meanproject\server\models\user.js:37:16)
at Immediate._onImmediate (C:\Users\ajaks\Desktop\meanproject\node_modules\bcryptjs\dist\bcrypt.js:153:21)
at runCallback (timers.js:810:20)
at tryOnImmediate (timers.js:768:5)
at processImmediate [as _immediateCallback] (timers.js:745:5)
[nodemon] app crashed - waiting for file changes before starting...
There is problem in creating of addUser function. To create a custom function inside mongoose model need to use statics
const UserSchema = mongoose.Schema({
name: {
type: String
},
email: {
type: String,
required: true
},
username: {
type: String,
required: true
},
password: {
type: String,
required: true
}
});
UserSchema.statics.addUser = function(newUser, callback) {
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(newUser.password, salt, (err, hash) => {
if (err) throw err;
newUser.password = hash;
newUser.save(callback);
});
});
};
module.exports = mongoose.model('User', UserSchema);
Now it will work. For more information how to create custom function inside mongoose models refer this
https://mongoosejs.com/docs/guide.html#statics
You can also read about the how module.exports work in nodejs
https://www.sitepoint.com/understanding-module-exports-exports-node-js/
Related
According to the error-trace the error occurs in the "validate" method, but as far as i see it my compare call is correct. I hope someone can explain to me why it happens anyways.
POST /user/register 500 23.312 ms - 2235
Error: data and hash arguments required
at Object.compare
at model.user_schema.methods.validate
The mongoose model:
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const salty = 10;
const user_schema = new mongoose.Schema({
name: {
type: String,
required: true,
unique: false,
minlength: 3,
maxlength: 32
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
}
});
user_schema.pre('save', (next) => {
// if the password is not changed, there is no need to hash it again
if (!this.isModified('password')) return next();
// hash the user password
bcrypt.hash(this.password, salty, (err, hash) => {
if (err) return next(err);
this.password = hash;
return next();
});
});
user_schema.methods.validate = (claim, callback) => {
// compare the password to the existing hash from the database
bcrypt.compare(claim, this.password, (err, is_match) => {
if (err) return callback(err);
return callback(null, is_match);
});
}
module.exports = mongoose.model('user', user_schema);
The router with the create call:
router.post('/register', (req, res, next) => {
let new_user = {
name: req.body.name,
email: req.body.email,
password: req.body.password
};
user_model.create(new_user, (err, user) => {
if (err) return next(err);
res.send(user);
});
});
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.
My Model Schema
const UserSchema = mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true
},
username: {
type: String,
required: true
},
password: {
type: String,
required: true
},
tweets: []
});
This are the methods i use to communicate with mongo
module.exports.getUserByUsername = function(username, callback){
const query = {username: username}
User.findOne(query, callback);
}
module.exports.addTweet = function(newTweet, newUser, callback){
User.updateOne(newUser, {$push: newTweet}, (err, isUpdate) => {
if(err) throw err;
callback(null, isUpdate)
});
}
Im using NodeJS to code my backend, i already register a user and a login but when i try to post a tweet with that user i get an error realted with the _id and i never use the ids.
router.post('/post', passport.authenticate('jwt', {session:false}), (req, res, next) => {
let newTweet = new User({
tweets:{
title: req.body.title,
body: req.body.body
}
})
User.getUserByUsername(req.body.username, (err, usert) => {
if(err) throw err;
if(!usert){
return res.json({success: false, msg: 'User not found'});
}
User.addTweet(newTweet, usert, (err, isUpdate) =>{
if(err) throw err;
if(isUpdate){
return res.json({success: true, msg: "Tweet Post"});
}
});
});
});
The Error
This is the error i get using PostMan
/home/daniel/react/miapp/Back/node_modules/mongodb/lib/utils.js:132
throw err;
^
MongoError: The field '_id' must be an array but is of type objectId in document {_id: ObjectId('5b26b4e911c67c4cfa6917e4')}
at Function.MongoError.create (/home/daniel/react/miapp/Back/node_modules/mongodb-core/lib/error.js:45:10)
at toError (/home/daniel/react/miapp/Back/node_modules/mongodb/lib/utils.js:149:22)
at /home/daniel/react/miapp/Back/node_modules/mongodb/lib/collection.js:1035:39
at /home/daniel/react/miapp/Back/node_modules/mongodb-core/lib/connection/pool.js:541:18
at _combinedTickCallback (internal/process/next_tick.js:131:7)
at process._tickCallback (internal/process/next_tick.js:180:9)
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
Your getUserByUsername() returns a document fromthe mongo collection like
{_id: Object("...."), .....}
If you just want the username add a project query to your getUserByUsername() as:
const project = {_id:0,username:1}
User.findOne(query, project,callback)
This returns only the username of the document.
Also change the definition of new tweet to:
let newTweet = {tweets: {title: req.body.title,body: req.body.body}}
Edit: What you can also do is let your getUserByUsername code as before instead change your updateOne code(define newTweet as mentioned above):
User.updateOne({_id: newUser._id}, newTweet, callback)
Ideally, you should project only the _id from the mongo collection and query it while updating as it not only saves you from the network throughout of retreiving unnecessary data but the update query is also fast due to indexing.
I am getting the correct data for friendRequests which is getting a user ID and throwing it in the friendRequest field of my mongoose file. When I add $push to add the data into the friendRequest array in the route file, it actually does not insert it and gives me back the err function I created.
Here is my route file:
exports.addContactPost = function(req, res, err) {
User.findByIdAndUpdate(req.signedCookies.userid, {
$push: {friendRequest: req.body.friendRequest}
}, function(err) {
if(err) {
console.log("post2");
return console.log('error');
}
else {
console.log('postsuccess');
res.json({response: true});
}
});
};
Here is the mongoose file:
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
bcrypt = require('bcrypt-nodejs'),
SALT_WORK_FACTOR = 10;
var UserSchema = new Schema({
email: { type: String, required: true, lowercase:true, index: { unique: true } },
password: { type: String, required: true },
firstName: {type: String, required: true},
lastName: {type: String, required: true},
phone: {type: Number, required: true},
birthday: {type: Date, required: true},
friendRequest: {type: Array},
friend: {type: Array}
});
UserSchema.pre('save', function(next) {
var user = this;
console.log("email exists");
// only hash the password if it has been modified (or is new)
if (!user.isModified('password')) return next();
// generate a salt
bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt) {
if (err) return next(err);
// hash the password along with our new salt
bcrypt.hash(user.password, salt, null, function(err, hash) {
if (err) return next(err);
// override the cleartext password with the hashed one
user.password = hash;
next();
});
});
});
UserSchema.methods.comparePassword = function(candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, function(err, isMatch) {
if (err) return cb(err);
cb(null, isMatch);
});
};
module.exports = mongoose.model('User', UserSchema);
So the document that mongo finds matching the provided userId does not have an array as its friendRequest property. Look at that specific document by ID in mongo shell and fix it so that friendRequest is an array.
I am unsure as to why I am getting an error for POST-ing a form. I am using mongoose to connect to mongodb and using Jade as my view engine. I am trying to POST an update and not a new account into the db, and using a cookie to pull the user's info. So it is the Edit User Profile page. Everything works on the Jade file and looks right, just when I hit the submit button it goes to a:
Cannot POST /editUserProfile
My mongoose User file:
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
bcrypt = require('bcrypt-nodejs'),
SALT_WORK_FACTOR = 10;
var UserSchema = new Schema({
email: { type: String, required: true, lowercase:true, index: { unique: true } },
password: { type: String, required: true },
firstName: {type: String, required: true},
lastName: {type: String, required: true},
phone: {type: Number, required: true},
birthday: {type: Date, required: true}
});
UserSchema.pre('save', function(next) {
var user = this;
console.log("email exists");
// only hash the password if it has been modified (or is new)
if (!user.isModified('password')) return next();
// generate a salt
bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt) {
if (err) return next(err);
// hash the password along with our new salt
bcrypt.hash(user.password, salt, null, function(err, hash) {
if (err) return next(err);
// override the cleartext password with the hashed one
user.password = hash;
next();
});
});
});
UserSchema.methods.comparePassword = function(candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, function(err, isMatch) {
if (err) return cb(err);
cb(null, isMatch);
});
};
module.exports = mongoose.model('User', UserSchema);
My route file:
exports.editUserProfile = function(req, res) {
User.findById(req.signedCookies.userid, function(err,user) {
if(err) {
res.send(err);
} else {
console.log(JSON.stringify(user));
res.render('editUserProfile', {title: 'Weblio',
ufirstName: user.firstName,
ulastName: user.lastName,
uemail: user.email,
uphone: user.phone,
ubirthday: user.birthday
});
//, user: user.firstName, - taken out after title
}
});
};
exports.editUserProfilePost = function(req, res) {
var updateUser = new User ({
firstName: req.body.firstName,
lastName: req.body.lastName,
email: req.body.email,
phone: req.body.phone,
birthday: new Date(req.body.birthday)
});
updateUser.save(function(err){
console.log("here 3");
if(!err) {
console.log("here3a");
res.render('userProfile', {title: 'Weblio'});
} else {
console.log("here 1a");
(new Error('Incorrect POST'));
return res.render('editUserProfileError', {title: 'Weblio'});
}
});
};
Jade file:
extends layout
block content
div.centerContent
form.validateForm(method="POST", action="/editUserProfile")
legend Edit Profile
input(type="text", name="firstName", maxlength="20", placeholder=ufirstName, value=ufirstName)
br
input(type="text", name="lastName", maxlength="20", placeholder=ulastName, value=ulastName)
br
input(type="email", name="email", maxlength="20", placeholder=uemail, value=uemail)
br
input(type="number", name="phone", maxlength="20", placeholder=uphone, value=uphone)
br
input(type="date", name="birthday", placeholder=ubirthday, value=ubirthday)
br
input.btn.btn-primary(type="submit", name="Update", value="Save")
a(href="/userProfile")
button.btn(type="button") Cancel
This is my app.js file: I have a bunch of other things in there but the Register part is getting POST-ed so I don't think the app.js has any problems.
app.get('/editUserProfile', user.editUserProfile);
app.post('/editUserProfile', user.editUserProfilePost);
Updated:
exports.editUserProfilePost = function(req, res, err) {
User.findByIdAndUpdate(req.signedCookies.userid,{
firstName: req.body.firstName,
lastName: req.body.lastName,
email: req.body.email,
phone: req.body.phone,
birthday: new Date(req.body.birthday)
}, function(err) {
if(!err) {
console.log("post2");
res.render('userProfile', {title: 'Weblio'});
} else {
console.log("post3");
(new Error('Incorrect POST'));
return res.render('editUserProfileError', {title: 'Weblio'});
}
});
};