I am trying to save a user to MongoDB database using post request as follow, but I got the error TypeError: User is not a function. I can't figure out anything wrong with it. The output says that error is present on the line " const user = new User(req.body);"
postman output nodejs output
is my userschema wrong or the export method is wrong.
const User = require("../models/user");
exports.signup = (req, res) => {
const user = new User(req.body);
user.save((err, user) => {
if (err) {
return res.status(400).json({
err: "NOT able to save user in DB"
});
}
res.json({
name: user.name,
email: user.email,
id: user._id
});
});
};
exports.signout = (req, res) => {
res.json({
message: "User signout"
});
};
//user schema
var mongoose = require("mongoose");
const crypto = require('crypto');
const uuidv1 = require('uuid/v1');
var userSchema = new mongoose.Schema({
name:{
type:String,
required:true,
maxlenght:32,
trim: true
},
lastname:{
type: String,
maxlenght:32,
trim: true
},
email:{
type: String,
trim: true,
required: true,
unique:true
},
userinfo:{
type:String,
trim:true
},
encry_password:{
type:String,
required: true,
},
salt:String,
role:{
type:Number,
default:0,
},
purchases :{
type:Array,
default:[]
}
} ,{ timestamps: true } );
userSchema.virtual("password")
.set(function(password){
this._password = password;
this.salt = uuidv1();
this.encry_password = this.securePassword(password);
})
.get(function(){
return this._password;
})
userSchema.methods = {
authenticate: function(plainpassword){
return this.securePassword(plainpassword) === this.encry_password;
},
securePassword: function (plainpassword){
if(!plainpassword) return "";
try {
return crypto.createHmac('sha256',this.salt)
.update(plainpassword)
.digest('hex');
} catch (err) {
return "";
}
}
};
module.export = mongoose.model("User",userSchema)
Here is the issue with the code.
Line 1: const user = new User(req.body);
Line 2: user.save((err, user) => {
JS is now confused about user which is a constant variable to that of the user which is the return value of the save action.
So, to get rid of this, rename the return value of save action to something else like responseUserObj. Thus your above two lines of code should now be
const user = new User(req.body);
user.save((err, responseUserObj) => {
Happy coding.
Related
I'm trying to follow the MVC architectural pattern and do all of my validation in my Mongoose model, rather than my controller.
I'm wondering how I can set error codes and truly custom error messages in my model (I.E. without the part that mongoose adds to the beginning of the message.)
At the moment my error message for the name field is: "message": "User validation failed: email: Please enter a valid email address", where it should be "Please enter a valid email address".
The response code from the server was 200 until I changed it in my errorHandlerMiddleware file, which is not ideal as it should be a 400 not the general 500.
So, somebody please help me to set the status code in my model and also make a custom error message.
Many thanks in advance!
const mongoose = require("mongoose");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const validator = require("validator");
const Schema = mongoose.Schema;
const UserSchema = new Schema(
{
name: {
type: String,
required: [true, "Please add a name"],
minLength: [3, "Name must be at least 3 characters"],
},
email: {
type: String,
required: [true, "Please add an email address"],
unique: [true, "It looks like you already have an account!"],
validate: {
validator: (value) => {
if (!validator.isEmail(value)) {
throw new Error("Please enter a valid email address");
}
},
},
},
password: {
type: String,
required: [true, "Please add a password"],
},
tokens: [
{
token: {
type: String,
required: true,
},
},
],
},
{ timestamps: true }
);
UserSchema.methods.toJSON = function () {
const user = this;
const userObject = user.toObject();
delete userObject.password;
delete userObject.tokens;
return userObject;
};
UserSchema.methods.generateAuthToken = async function () {
const user = this;
const token = jwt.sign({ _id: user._id.toString() }, process.env.JWT_SECRET, {
expiresIn: "7 days",
});
user.tokens = user.tokens.concat({ token });
await user.save();
return token;
};
UserSchema.statics.findByCredentials = async (email, password) => {
const user = await User.findOne({ email });
if (!user) {
statusCode(401);
throw new Error("Unable to login");
}
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) {
statusCode(401);
throw new Error("Unable to login");
}
return user;
};
UserSchema.pre("save", function (next) {
if (this.password.length < 6) {
throw new Error("Password must be at least 6 characters");
}
if (!this.isModified("password")) {
return next();
}
this.password = bcrypt.hashSync(this.password, 10);
return next();
});
module.exports = User = mongoose.model("User", UserSchema);
i need a real custom error code and message from mongoose
I decided to catch the errors in the try/catch block on the controller, as so:
try {
await user.save();
} catch (err) {
// Error handling for duplicate email address
if (err.code === 11000) {
return res.status(400).send("It looks like you already have an account.");
}
// Error handling for misc validation errors
if (err.name === "ValidationError") {
res.status(400);
return res.send(Object.values(err.errors)[0].message);
}
}
I am writing the Model for my Web App API, and am getting the following circular dependency error:
Warning: Accessing non-existent property 'deleteMany' of module exports inside circular dependency
(Use node --trace-warnings ... to show where the warning was created)
.
Here is my code:
const validator = require('validator')
const bcrypt = require('bcrypt')
const jwt = require('jsonwebtoken')
const Task = require('./task')
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const userSchema = new Schema({
email: {
type: String,
unique: true,
required: true,
trim: true,
lowercase: true,
validate(value) {
if (!validator.isEmail(value)) {
throw new Error('Email is invalid.')
}
}
},
password: {
type: String,
required: true,
trim: true,
minLength: 8
},
name: {
type: String,
unique: true,
required: true,
trim: true
},
tokens: [{
token: {
type: String,
required: true
}
}]
})
userSchema.pre('save', async function(next) {
const user = this
if (user.isModified('password')) {
user.password = await bcrypt.hash(user.password, 8)
}
next() // run the save() method
})
userSchema.pre('deleteOne', {document: true, query: false}, async function(next) {
const user = this
await Task.deleteMany({owner: user._id})
next()
})
userSchema.methods.toJSON = function() {
const user = this
const userObject = user.toObject()
delete userObject.password
delete userObject.__v
delete userObject.tokens
return userObject
}
userSchema.methods.generateAuthToken = async function () {
const user = this
const token = jwt.sign({ _id: user._id.toString() }, process.env.JSON_WEB_TOKEN_SECRET)
user.tokens = user.tokens.concat({ token })
await user.save()
return token
}
userSchema.statics.findByCredentials = async (email, password) => {
const user = await User.findOne({email})
if (!user) {
throw new Error('Unable to login')
}
const isMatch = await bcrypt.compare(password, user.password)
if (!isMatch) {
throw new Error('Unable to login')
}
return user
}
userSchema.virtual('tasks', {
localField: '_id',
foreignField: 'owner',
ref: 'Task'
})
const User = mongoose.model('User', userSchema);
module.exports = User
Any idea what could be going wrong? I have checked my Node.js and MongoDB versions and updated them, but continue to get this same error when I try to delete. I can provide further details of my code if necessary. The problem area in question is the one leading with userScheme.pre('deleteOne'....
Working on a personal project, one of the functions of the project is to update the user status on what event they are participating.
i wanted to submit a value using a button
<form action="/users/fooddrivebanner" method="POST"><button name="fooddrive" type="submit" value="fooddrive" id="fooddrive">Participate</button></form>
then pass the value to my route and save it inside my database
router.post('/fooddrivebanner', (req,res)=>{
const { fooddrive } = req.body;
const _id = ObjectId(req.session.passport.user._id);
User.findOne({ _id: _id }).then((user)=>{
if (!user) {
req.flash("error_msg", "user not found");
res.redirect("/fooddrivebanner");
}
if (typeof eventparticpating !== "undefined") {
user.eventparticpating = 'fooddrive';
}
user.save(function (err, resolve) {
if(err)
console.log('db error', err)
// saved!
});
})
.catch((err) => console.log(err));
Here is the User model
const mongoose = require('mongoose');
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
},
eventparticpating: {
type: String,
default: 'None At The Moment'
}
});
const User = mongoose.model('User', UserSchema);
module.exports = User;
It showed a console error
TypeError: Cannot set property 'eventparticpating' of null
UPDATE
Edit 1:
I followed Mr Gambino instructions, error Gone yet cannot update the database, how would i be able to adjust and find my user?
Instead of saving within the findOne function,you can do this:
router.post('/fooddrivebanner', async (req,res) => {
const { fooddrive } = req.body;
const _id = ObjectId(req.session.passport.user._id);
await User.findOne({ _id: _id }, (error, user) => {
if (error) {
req.flash("error_msg", "user not found");
res.redirect("/fooddrivebanner");
}
}).updateOne({ eventparticpating: "foodrive" });
});
I hope that answers your question
I am trying to delete two record from different database (practice log, student) within the same session, the problem is the practice log record is found successfully and shown in console.log(deletePracticeLog), but it cannot be remove from the database. anyone know what have I done wrong? I am very confused.
const deleteStudentById = async (req, res, next) => {
const sid = req.params.sid;
let deleteStudent;
let deletePracticeLog;
try {
deleteStudent = await Students.findById(sid).populate("teacher");
} catch (err) {
const error = new HttpError("Delete failed, please try again.", 500);
return next(error);
}
try {
deletePracticeLog = await PracticeLog.find({ student: sid }).exec();
console.log(deletePracticeLog); // **<-this correctly show the record needed to be deleted, it's a object within an array**
} catch (err) {
const error = new HttpError("Cannot delete, please try again.", 500);
return next(error);
}
if (!deleteStudent) {
const error = new HttpError("Could not find student for this id.", 404);
return next(error);
}
try {
const sess = await mongoose.startSession();
sess.startTransaction();
await deleteStudent.remove({ session: sess }); //delete the student from student database
deleteStudent.teacher.students.pull(deleteStudent); //delete the student id from teacher database
await deleteStudent.teacher.save({ session: sess });
await deletePracticeLog.remove({ session: sess }); // **<-this line caused error, when this line is removed, other code run successfully and other record are deleted.**
await sess.commitTransaction();
} catch (err) {
const error = new HttpError("Cannot delete student with provided id.", 500);
return next(error);
}
res.status(200).json({ message: "Deleted student." });
};
when you are using await, you don't need to exec()
delete your exec() from
deletePracticeLog = await PracticeLog.find({ student: sid }).exec();
change to
deletePracticeLog = await PracticeLog.find({ student: sid })
beacuse you use exec(), use this code for remove data
await deletePracticeLog[0].remove({ session: sess })
if the result of PracticeLog.find({ student: sid }) is one Array and you want to delete all of them, you have to use deleteMany like this:
PracticeLog.deleteMany({ student: sid }).session(sess)
it's worked for me
here is the Mongoose schema:
const mongoose = require("mongoose");
const studentsSchema = new mongoose.Schema({
name: { type: String, require: true },
email: { type: String, require: true },
contact: { type: String, require: true },
teacher: { type: mongoose.Types.ObjectId, require: true, ref: "teachers" },
practiceLogs: [
{ type: mongoose.Types.ObjectId, require: true, ref: "practice_logs" },
],
});
module.exports = mongoose.model("students", studentsSchema);
const practiceLogSchema = new mongoose.Schema({
repertoire: { type: String, require: true },
student: { type: mongoose.Types.ObjectId, require: true, ref: "students" },
teacher: { type: mongoose.Types.ObjectId, require: true, ref: "teachers" },
});
module.exports = mongoose.model("practice_logs", practiceLogSchema);
here is the mongoose.connect():
const url =
"myURL";
mongoose
.connect(url, { useUnifiedTopology: true, useNewUrlParser: true })
.then(() => {
app.listen(5000);
console.log("connected.");
})
.catch((err) => {
console.log(err);
});
I have a user I can save in MongoDB, when I enter correct data, the save works.
But when I enter wrong data, I can't catch the errors to be seen for the user. All I can see is this on the code editor:
...UnhandledPromiseRejectionWarning: ValidationError: User validation
failed: username: username is not there!...
This error "kills" the server, and so I'm not rendering home-guest template.
The question is how I can catch the errors and show them to the user?
Schema:
const mongoose = require("mongoose")
const userSchema = new mongoose.Schema({
username: {
type: String,
required: [true, "username is not there!"],
minlength: 3,
maxlength: 20,
},
email: {
type: String,
required: true,
},
password: {
type: String,
required: true,
minlength: 6,
maxlength: 20,
},
})
module.exports = mongoose.model("User", userSchema)
Controller:
const mongoose = require("mongoose")
const userModel = require("../models/userModel")
exports.signUp = async (req, res) => {
const { username, email, password } = req.body
try {
const user = await new userModel({
username,
email,
password,
})
user.save()
} catch (error) {
res.render("home-guest", { error })
}
}
You just need to add an await to the save operation, since that's also async:
const mongoose = require("mongoose")
const userModel = require("../models/userModel")
exports.signUp = async (req, res) => {
const { username, email, password } = req.body
try {
const user = await new userModel({
username,
email,
password,
})
// Wait for the save to complete, also allowing you to catch errors
await user.save()
} catch (error) {
res.render("home-guest", { error })
}
}
EDIT: And note that you do not need an async in front of new userModel(). new cannot return a promise, it is always synchronous.