How this pre hook save works? - node.js

I have been trying to use populate in mongoose, and my current idea is to check if a particular document is present in MongoDB, if it is then, the pre-save hook should check and rather then creating a new Document, it should just push it to the refs, so that I can populate later.
Now, the ref is being saved in MongoDB, however, how to check if the document which I am trying to save is there in mongo in pre save(or any other more suitable method)
This is the schema.
var Userschmea = new mongoose.Schema({
user:String,
posts:[{
type:mongoose.Schema.Types.ObjectId,
ref:'Post'
}]
})
var PostSchema = new mongoose.Schema({
content:String,
author:[{
type:mongoose.Schema.Types.ObjectId,
ref:'Author'
}]
})
Userschmea.pre('save',(next)=>{
//what to do here
next()
})
var Post = mongoose.model('Post',PostSchema);
var User = mongoose.model('User',Userschmea);
This is the endPoint by which I am trying to save:
app.post('/save/user',(req,res)=>{
console.log(req.body);
//Can i access this in my pre-save
const newUser = new User({
user: req.body.user
})
newUser.save((err)=>{
const newPost = new Post({
content:req.body.content,
author: newUser._id
})
newPost.save((err)=> {
if(err) {
return res.send(err);
}
})
if(err){
console.log(err);
return res.send(err);
}
})
return res.send(req.body.user);
})

You can use findOne method with some unique key to check whether user is exists or not. I have used here email.
Userschmea.pre('save',(next)=>{
var self = this;
Userschmea.findOne({email : this.email}, 'email', function(err, results) {
if(err) {
next(err);
} else if(results) {
self.invalidate("user", "user is exists");
next(new Error("User is already exists"));
} else {
next();
}
});
})
Hope this help!
====== UPDATE =====
Q: this is coming empty.
Ans:
Function expressions are best for object methods while Arrow functions are best for callbacks or methods like map, reduce, or forEach.
So, Don't use arrow function in this case to access global scope.

Related

Mongoose: Built in validations on additional properties of SchemaType not working like min, max, minlength, maxlength etc

I am new to mongoose (MongoDB) and Nodejs, i am creating RestFul services for CRUD operations, however my issue is, for the Schema expression, the SchemaType additional properties are not being considered in the built in validation of mongoose where as it is considering required property only. Please find my model below for your reference:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
let ProductSchema = new Schema({
name: {
type: String,
required: true,
minlength:2,
maxlength:10
},
price:{
type: Number,
required:true,
min:2,
max:100
},
})
//Export the model
module.exports = mongoose.model('Product', ProductSchema);
So in the above model, There are properties like min, max for Number data Type and minlength and maxLength for String data Type which are not at all considered for the validation to take place before saving into the MongoDB.
Am i missing any configuration in this? i have gone through the mongoose documentation and also through the stackoverflow's lot of posts but i did not get any information regarding this particularly.
Here is my controller as well:
const Product = require('../models/product.model');
// Simple version, without validation or sanitation
exports.test = function (req,res) {
res.send('Greetings from the Test Controller!');
};
exports.product_create = function (req,res,next) {
let product = new Product(
{
name: req.body.name,
price: req.body.price
}
);
product.save(function (err) {
if(err){
return next(err);
}
res.send('Product created Successfully');
}
)
};
exports.product_details = function (req,res, next) {
Product.findById(req.params.id, function (err, product){
if(err) {
return next(err);
}
res.send(product);
})
};
exports.product_update = function (req, res, next) {
Product.findOneAndUpdate(req.params.id, {$set: req.body},opts, function (err, product){
if(err) return next(err);
res.send('Product Updated');
})
};
exports.product_delete = function (req,res,next) {
Product.findOneAndRemove(req.params.id, function (err) {
if(err) return next(err);
res.send('Deleted product');
})
};
Please guide me if i am missing something, still that needs to incorporate into it. Thanks.

Am I doing this API Put request correct with mongoose?

So I'm using mongoose and mongodb and express routing to do it.
I have a user document with an empty array on creation- "todolist", I want to be able to edit this to add a task to the list as well as edit those tasks.
Right now I'm thinking the best way to do it is have the server check if the req.body has certain variables set to know which part of the document I need to edit.
Is this the correct approach or am I suppose to create a new API route for something like this or am I doing it correctly by checking req variables from the client to determine what I want to edit on the server? Is there a cleaner way to do this too?
router.put('/:user_id', function(req, res) {
User.findById(req.params.user_id, function(err, user){
if(err){
res.send(err);
return err;
}
if(req.body.createTask) {
user.todolist.push({
"task_name": req.body.task_name,
"task_importance": req.body.importance,
"task_completed": false
})
}else if(req.body.edit_task){
if(req.body.edit_task_name) {
user.todolist[req.body.index].task_name = req.body.new_task_name;
}else if(req.body.edit_task_importance) {
user.todolist[req.body.index].task_importance = req.body.new_task_importance;
}else if(req.body.edit_task_completed) {
user.todolist[req.body.index].task_completed = true;
}
}
user.save(function(err){
if(err)
res.send(err);
res.json({message: 'User updated'});
})
})
});
I would recommend you to model your database with proper entities, creating a Task model separately from User and adding a reference to User, like this:
var TaskSchema = new mongoose.Schema({
user: {
type: mongoose.Schema.ObjectId,
ref: 'User'
},
name: String,
importance: String,
completed: Boolean
});
If you want to, you can also add reference to Task on User:
var User = new mongoose.Schema({
// note this is an array
todolist: [{
type: mongoose.Schema.ObjectId,
ref: 'Task'
}]
});
Doing that, to make things simpler, you should create a pre save hook to automatically add tasks to user:
TaskSchema.pre('save', function(next) {
if (this.isModified('user') && this.user) {
// $addToSet will add this document to the user's todolist (if it's not already there)
User.findOneAndUpdate({_id: this.user}, {$addToSet: {todolist: this}}, function(err, doc) {
next(err);
})
} else {
next();
}
});
This way the correct approach would be provide endpoints correlated to entities. So, to create a task, a user should request POST /tasks with body { user: '123', name: 'name', ... }:
router.post('/tasks', function(req, res) {
Task.create(req.body, function(err, task) {
if (err) return res.status(500).send(err);
return res.status(201).json(task);
});
});
It will automatically add the task to the user 123 with our pre save hook.
To edit the task, just make that on task endpoint:
router.put('/tasks/:id', function(req, res) {
Task.findById(req.params.id, function(err, task) {
if (err) return res.status(500).send(err);
if (!task) return res.status(404).send('Not Found');
task.name = req.body.name || task.name;
task.importance = req.body.importance || task.importance;
task.completed = true;
task.save(function(err, task) {
if (err) return res.status(500).send(err);
res.status(200).json(task);
});
});
});
You don't need to edit user as it only keeps tasks reference.
If you want to get users with tasks data populated (and not only their ids), just do:
router.get('/users', function(req, res) {
User.find({}).populate('todolist').exec(function(err, users) {
...
});
});
For queries like that above, I recommend you to use some library like querymen, which parses querystrings (e.g. GET /users?q=Some+Search&limit=10&page=2) to MongoDB query arguments.
Hope it helps. :)

update document using a unique filed with findOneAndUpdate is thorwing exception

I am new to node.js and mean.
I have nearly 20 fields to update in a document.
var UserSchema = mongoose.model('UserSchema');
I am passing these 20 fields in the request body and updating the UserSchema model object.
var newUser =new UserSchema(req.body);
Actually i am not passing _id from my request body but when i saw my newUser._id it is filled up with some id, so it resulting in an error called
exception: After applying the update to the document {_id: ObjectId('560e506ad4369d2a02a6ad6d') , ...}, the (immutable) field '_id' was found to have been altered to _id: ObjectId('560e65f395857e210313d3a7')
UserSchema.findOneAndUpdate({ userName: newUser.userName }, newUser, { new: true }, function (err, newuser) {
if (err) {
console.log(err);
return res.status(400).json({ message: err });
}
return res.status(200).json({ result: newUser });
});
Because you are creating new record set by following statement,
var newUser =new UserSchema(req.body);
Above statement will match all provided fields by req.body with registered schema for UserSchema Model and will generate new ObjectId for it. So newUser variable have _id field and when you try update existing record with newUser, it causes error as you are trying to change immutable _id field.
Instead you can re-write whole as following,
var UserSchema = mongoose.model('UserSchema');
var newUser = req.body;
UserSchema.findOneAndUpdate({ userName: newUser.userName }, newUser, { new: true }, function (err, newuser) {
if (err) {
console.log(err);
return res.status(400).json({ message: err });
}
return res.status(200).json({ result: newUser });
});
And if provided record does not exists then findOneAndUpdate will insert new as you provided new: true flag.

node.js email verification token

I'm trying to set up a verification step in Mongoose, Express Node application based on this blog post ... http://danielstudds.com/adding-verify-urls-to-an-express-js-app-to-confirm-user-emails-secure-spa-part-6/ that post is over a year old so it kind of surprises me that its the first google result for 'node email verification'. I'm very new to node so I'm reliant on examples. Based on that post I did not see a download so I pieced it together to suit my scenario and here is what my code looks like.
Verification Model
var mongoose = require('mongoose'),
uuid = require('node-uuid'),
User = require('mongoose').model('User');
var verificationTokenSchema = new mongoose.Schema({
_userid : { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
token: {type: String, required: true},
createdAt: {type: Date, required: true, default: Date.now, expires: '4h'}
});
verificationTokenSchema.methods = {
createVerificationToken : function (done) {
var verificationToken = this;
var token = uuid.v4();
verificationToken.set('token', token);
verificationToken.save( function (err) {
if (err) return done(err);
return done(null, token);
});
}
};
exports.verifyUser = function(token, done) {
verificationTokenModel.findOne({token: token}, function (err, doc){
if (err) return done(err);
User.findOne({_id: doc._userId}, function (err, user) {
if (err) return done(err);
user["verified"] = true;
user.save(function(err) {
done(err);
});
});
});
};
var verificationTokenModel = mongoose.model('VerificationToken', verificationTokenSchema);
exports.verificationTokenModel = verificationTokenModel;
Then in my User model I call create like so..
User Model
exports.createUser = function(req, res, next) {
// Do all the stuff that creates the user save it and get the id back
var verificationToken = new verificationTokenModel({_userId: user._id});
verificationToken.createVerificationToken(function (err, token) {
if (err){
err = new Error("Couldn't create verification token");
res.status(400);
return res.send({reason:err.toString()});
}
// Do stuff with the token here and email
This works 'partially' in my db 'verificationtokens' collection the objects don't contain a _userid they contain the _userid (user._id) stored in _id
My first issue is I don't really understand how this works when there isn't a 'constructor'
var verificationToken = new verificationTokenModel({_userId: user._id});
and how do I get this to store the user._id as _userid in the verification collection
I don't use Mongoose, but here is what it happened:
For your first question:
when you run:
var verificationTokenModel = mongoose.model('VerificationToken', verificationTokenSchema);
it creates the constructor.
For the second question:
MongoDB always create documents with a field called "_id", so, the "_id" field in your verification collection is not the "_id" field from your User collection. The reason that _userid is not inserted is because you have a typo:
var verificationToken = new verificationTokenModel({_userId: user._id});
where "_userId" should be "userid"

can't populate the array with mongoose in node.js

This is my schema on the course
var CourseSchema = mongoose.Schema({
students:[{ type: ObjectId, ref: 'User' }]
});
var CourseModel = mongoose.model('Course',CourseSchema);
var UserSchema = mongoose.Schema({ name:String})
var UserModel = mongoose.model('User',UserSchema);
In the mongodb, I have created the existing courses and users, and when the user want to participate the course, the user reference will be added to the students array in the course model.
Here is how I try to add the user reference to the students
function joinCourse(cid,uid,callback){
var options = { new: false };
var uid = mongoose.Types.ObjectId(uid);
CourseModel.findOneAndUpdate({'_id':cid},{'$addToSet':{'students':uid}},options,function(err,ref){
if(err) {
console.log('update joinCourse'.red,err);
callback(err, null);
}else{
console.log('update joinCourse '.green+ref);
callback(null,ref);
}
})
}
when the above function is executed, the students array has the objectID or reference of the user. However, when I want to populate the students from the course model, it doesn't work.
var id = mongoose.Types.ObjectId(id);
CourseModel.findById(id).populate('students').exec(function(err, users) {
if(err){callback(err, null);}
else{
//// users.length == undefined
console.log("findCourseStudentsById".green,users.length);
callback(null, users);
}
})
I didn't find any problem on populate function, so I wonder is there something wrong with joinCourse function? so I change the function as
courseModel.findCourseById(cid,function(err,course){
if(err) next(err);
else{
course.students.push({'_id': mongoose.Types.ObjectId(uid)});
course.save(function (err) {
if (err) next(err);
});
}
})
but still the populate doesn't work. Note, I am using mongoose 3.6
populate populates the model instance, but the callback is passed the model instance on which you call populate, and not the populated data itself:
CourseModel.findById(id).populate('students').exec(function(err, course) {
if(err){callback(err, null);}
else{
console.log("findCourseStudentsById".green, course.students.length);
callback(null, course);
}
});

Resources