Validation Error in mongoDB while making a reference to another Schema? - javascript

I have created a blog schema in mongo which makes reference to the user schema.
However, when I try to save the blog in MongoDB I get the following error: -
CUrrrent post user: new ObjectId("61d28db34c78f60375189033")
User validation failed: passwordHash: Path `passwordHash` is required., name: Path `name` is required., username: Path `username` is required.
I am sending this via JSON
{
"title": "Best Copywriting formulas!",
"author": "Copywriters Inc.",
"url": "https://buffer.com/resources/copywriting-formulas/",
"likes": 420
}
I am unable to decode why I am getting this validation error when I am adding nothing new to the User schema.
Here is my main router code: -
blogRouter.post('/', async (request, response) => {
const blog = new Blog(request.body)
if (blog.author === undefined || blog.title === undefined)
return response.status(400).json({
error: "name or title missing!"
})
//temporary get the first user from the Users db
const userDB = await User.find({});
//Get the first available user in db
const currentUser = userDB[0]._id;
console.log('CUrrrent post user: ', currentUser);
const newBlog = new User({
title: request.body.title,
author: request.body.author,
url: request.body.url,
likes: request.body.likes || 0,
user: currentUser
})
try {
const newEntry = await newBlog.save()
response.status(200).json(newEntry);
} catch (error) {
logger.error(error.message);
}
})
My Blog Schema: -
const blogSchema = new mongoose.Schema({
title: String,
author: String,
url: String,
likes: {
type: Number,
default: 0
},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}
})
blogSchema.set('toJSON', {
transform: (document, returnedObject) => {
returnedObject.id = returnedObject._id.toString()
delete returnedObject._id
delete returnedObject.__v
}
})
module.exports = mongoose.model('Blog', blogSchema)
Here is my user Schema: -
var uniqueValidator = require('mongoose-unique-validator');
const userSchema = new mongoose.Schema({
username: {
type: String,
required: true,
minLength: 3,
unique: true
},
name: {
type: String,
required: true
},
passwordHash: {
type: String,
required: true
}
})
userSchema.plugin(uniqueValidator, {message: 'username already taken. {VALUE} not available.'});
userSchema.set('toJSON', {
transform: (document, returnedObject) => {
returnedObject.id = returnedObject._id.toString()
delete returnedObject._id
delete returnedObject.__v
delete returnedObject.passwordHash
}
})
const User = mongoose.model('User', userSchema);
module.exports = User

You should change your model name when creating a new Blog document:
const newBlog = new Blog({
title: request.body.title,
author: request.body.author,
url: request.body.url,
likes: request.body.likes || 0,
user: currentUser,
});
Also, a good practice would be to check if there are any users in the database before retrieving the first one.
This to avoid possible index out of bounds exceptions:
const userDB = await User.find({});
if (userDB.length > 0) {
const currentUser = userDB[0]._id;
...

Related

Nodejs - TypeError stating that the hashPassword function is not a function, how can I solve it?

how is everything ? I really need your help!
I'm building an API to register authenticated users, with storage in the mongo atlas database (cloud). I'm currently experiencing the following error: TypeError subscription error: User.hashPassword is not a function. I've done several researches, in several questions here on stackoverflow and on other sites, after testing all the solutions the error persists.
my user.model.js file looks like this:
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const bcrypt = require('bcrypt');
const userSchema = new Schema(
{
userName: { type: String, unique: true, required: true },
email: { type: String, required: true, unique: true },
emailToken: { type: String, default: null },
emailTokenExpires: { type: Date, default: null },
active: { type: Boolean, default: false},
password: { type: String, required: true},
resetPasswordToken: { type: String, default: null },
resetPasswordExpires: { type: Date, default: null },
emailToken: {type: String, default: null},
emailTokenExpires: {type: Date, default: null},
},
{
timestamps: {
createdAt: "createdAt",
updatedAt: "updatedAt",
},
}
);
const User = mongoose.model("user", userSchema);
module.exports.hashPassword = async (password) => {
try {
const salt = await bcrypt.genSalt(10); // 10 rounds
return await bcrypt.hash(password, salt);
} catch (error) {
throw new Error("Hashing failed", error);
}
};
module.exports = User;
and my user.controller.js file looks like this:
const Joi = require("joi");
require("dotenv").config();
const { v4: uuid } = require("uuid");
const { sendEmail } = require("./helpers/mailer");
const User = require("./user.model");
//Validate user schema
const userSchema = Joi.object().keys({
email: Joi.string().email({ minDomainSegments: 2 }),
password: Joi.string().required().min(4),
confirmPassword: Joi.string().valid(Joi.ref("password")).required(),
});
exports.Signup = async (req, res) => {
try {
const result = userSchema.validate(req.body);
if (result.error) {
console.log(result.error.message);
return res.json({
error: true,
status: 400,
message: result.error.message,
});
}
//Check if the email has been already registered.
var user = await User.findOne({
email: result.value.email,
});
if (user) {
return res.json({
error: true,
message: "Email is already in use",
});
}
const hashPassword = await User.hashPassword(result.value.password);
const id = uuid(); //Generate unique id for the user.
result.value.userId = id;
//remove the confirmPassword field from the result as we dont need to save this in the db.
delete result.value.confirmPassword;
result.value.password = hashPassword;
let code = Math.floor(100000 + Math.random() * 900000); //Generate random 6 digit code.
let expiry = Date.now() + 60 * 1000 * 15; //Set expiry 15 mins ahead from now
const sendCode = await sendEmail(result.value.email, code);
if (sendCode.error) {
return res.status(500).json({
error: true,
message: "Couldn't send verification email.",
});
}
result.value.emailToken = code;
result.value.emailTokenExpires = new Date(expiry);
const newUser = new User(result.value);
await newUser.save();
return res.status(200).json({
success: true,
message: "Registration Success",
});
} catch (error) {
console.error("signup-error", error);
return res.status(500).json({
error: true,
message: "Cannot Register",
});
}
};
Error displayed in terminal:
Danilo#DANILO-PC D:\Meus Documentos\Área de Trabalho\api-auth-pokestore
$ node app.js
Server started listening on PORT : 5000
Database connection Sucess.
signup-error TypeError: User.hashPassword is not a function
at exports.Signup (D:\Meus Documentos\Área de Trabalho\api-auth-pokestore\src\users\user.controller.js:39:37)
at processTicksAndRejections (internal/process/task_queues.js:94:5)
image terminal
Look at this part of your code:
module.exports.hashPassword = async (password) => { ... };
module.exports = User;
You're setting hashPassword in your exports, then completely replacing your exports with User. You probably wanted to do something like this instead:
User.hashPassword = async (password) => { ... };
module.exports = User;
or move your module.exports.hashPassword = ... so it's after the module.exports = ....

How can I use function getPublicFields with populate mongoose?

Here is the function getPublicFields in User Schema
User Schema
UserSchema.methods.getPublicFields = function () {
var returnObject = {
firstName: this.firstName,
lastName: this.lastName,
email: this.email,
_id: this._id,
};
return returnObject;
};
here just I connect the User Schema with the product and they gave me all the user Data watch I don't want
productController.Js
exports.getProducts = async (req, res, next) => {
try {
const products = await Product.find().populate("owner");
res.status(200).send(products);
} catch (e) {
next(e);
}
};
Product Schema
var mongoose = require("mongoose");
var { Schema } = mongoose;
const ProductSchema = new Schema({
title: {
type: String,
},
category: {
type: String,
},
price: {
type: Number,
},
completed: {
type: Boolean,
default: false,
},
owner: {
ref: "User",
type: mongoose.Schema.Types.ObjectId
},
img: {
type : Array,
}
});
module.exports = mongoose.model("Product", ProductSchema);
populate will give you a plain object, not a Mongoose instance. What you can do is construct a User instance from it:
const user = new User(product.owner);
product.owner = user.getPublicFields();

findOneandReplace keeps giving error: "Error: The replacement document must not contain atomic operators."?

I am currently developing a Pokemon Team Builder app with a React frontend and an Express backend with MongoDB for the database.
As far as I can tell my TeamSchema has no such atomic operators? Here is my TeamSchema:
const mongoose = require('mongoose');
const TeamSchema = new mongoose.Schema({
name: {
type: 'String',
required: true,
unique: true,
},
team: [
{
name: { type: String },
types: [{ type: String }],
sprite: { type: String },
},
],
username: {
type: String,
required: true,
},
userId: {
type: String,
required: true,
},
});
const TeamModel = mongoose.model('Team', TeamSchema);
module.exports = TeamModel;
And the error gets thrown in this method when I attempt to call the findOneAndReplace method by finding a team that has a name and userId match.
const replaceTeam = async (req, res) => {
const { teamName: name, filteredTeam: team } = req.body;
const { username, _id: userId } = req.user;
const newTeam = new Team({ name, team, username, userId });
try {
const replacedTeam = await Team.findOneAndReplace({ name, userId }, newTeam);
console.log(replacedTeam);
res.status(200).json({ message: 'Team was successfully overwritten!' });
} catch (err) {
console.log(err);
res.status(500).json({ message: 'An error occurred while updating the team.' });
}
};
This has been a real headscratcher here and I am not sure what is going wrong here. I have only started using mongoose a couple of weeks ago, so I wonder if it's something fundamental I am misunderstanding here.
The Mongoose function findOneAndReplace expects a document object passed in. See the below code.
details.findOneAndReplace(
{ location: "New York" },
{ name: "Sunny", age: 292, location: "Detroit" },
function(err, result) {
if (err) {
res.send(err);
} else {
res.send(result);
}
}
);
Change
const newTeam = new Team({ name, team, username, userId })
to
const newTeam = {name, team, username, userId}
Also as in the other poster's code, add the new: true option to the call as follows by changing
const replacedTeam = await Team.findOneAndReplace({ name, userId }, newTeam);
to
const replacedTeam = await Team.findOneAndReplace({ name, userId }, newTeam, { new: true });
otherwise the original document will be returned into replacedTeam
You can just use findOneAndUpdate and update all the fields with new data. You can do it like this:
const replaceTeam = async (req, res) => {
const { teamName: name, filteredTeam: team } = req.body;
const { username, _id: userId } = req.user;
try {
const replacedTeam = await Team.findOneAndUpdate({ name, userId }, { name, team, username, userId }, {new: true});
console.log(replacedTeam);
res.status(200).json({ message: 'Team was successfully overwritten!' });
} catch (err) {
console.log(err);
res.status(500).json({ message: 'An error occurred while updating the team.' });
}
};

Mongoose Populate to get all posts by user returns empty array

I'm attempting to get a better grasp on Express.js, and am trying to create a simple blogging site.
My user model is simple: Username, Display Name, and an array of posts.
const userSchema = new Schema({
username: {
type: String,
required: true,
unique: true,
trim: true,
minlength: 3
},
displayname: {
type: String,
required: true,
minlength: 1
},
posts: [{ type: Schema.Types.ObjectId, ref: 'Post' }]
}, {
timestamps: true,
});
The same goes for my Post model: content and author.
const postSchema = new Schema({
body: {
type: String,
required: true,
minlength: 1,
maxlength: 140
},
author: { type: Schema.Types.ObjectId, ref: 'User', required: true }
});
I've successfully created a new user and post:
[
{
"posts": [],
"_id": "5d32c9474e28f66c08119198",
"username": "Prince",
"displayname": "The Artist",
"createdAt": "2019-07-20T07:56:55.405Z",
"updatedAt": "2019-07-20T07:56:55.405Z",
"__v": 0
}
]
[
{
"_id": "5d34af1ecae7e41a40b46b5a",
"body": "This is my first post.",
"author": "5d32c9474e28f66c08119198",
"__v": 0
}
]
Here's my routes for creating a user and post
//Create a user
router.route('/create').post((req, res) => {
const username = req.body.username;
const displayname = req.body.displayname;
const newUser = new User({
username,
displayname
});
newUser.save()
.then(() => res.json('New user created!'))
.catch(err => res.status(400).json(`Error: ${err}`));
});
//Create A Post
router.route('/create').post((req, res) => {
const body = req.body.body;
const author = req.body.author;
const newPost = new Post({
body,
author
});
newPost.save()
.then(() => res.json('Created new post!'))
.catch(err => res.status(400).json(`Error: ${err}`));
});
And my method to get all posts by a user:
//Get all posts by user
router.route('/posts/:id').get((req, res) => {
User.findById(req.params.id)
.populate('posts')
.exec((err, user) => {
if(err) {
res.status(400).json(`Error: ${err}`);
} else {
res.json(user.posts);
}
});
});
Whenever I look at the response from /users/posts/:id, the array is empty, but I would expect it to be filled with the post made by the user with the ID I supply? Am I misunderstanding how populate works?
You need to add the posts id to the users post list in order for mongoose's populate to work properly.
router.post('/create' async (req, res) => {
const body = req.body.body;
const author = req.body.author;
const newPost = new Post({
body,
author
});
try {
const user = await User.findById(author);
const post = await newPost.save()
user.posts = user.posts.concat(post._id)
await user.save()
return res.json(post.toJSON())
} catch (e) {
return res.status(400).json(`Error: ${e}`));
}
});

Mongoose assign a collection to another

I am trying to add a post to a user collection after the user was created with empty posts. I have tried with populate with no success .. any help is much appreciated.
// Post Model
const mongoose = require('mongoose');
const { Schema } = mongoose;
const UserModel = require('./user-model');
let PostSchema = new Schema({
author: {
ref: 'users',
type: Schema.Types.ObjectId
},
content: String,
description: String,
date: {
default: new Date(),
type: Date
},
title: String,
updatedAt: {
default: new Date(),
type: Date
}
});
let PostModel = mongoose.model('posts', PostSchema);
module.exports = PostModel;
// User Model
const mongoose = require('mongoose');
const { Schema } = mongoose;
const PostModel = require('./post-model');
let UserSchema = new Schema({
name: {
type: String
},
email: {
lowercase: true,
type: String,
trim: true,
unique: true
},
password: {
type: String,
},
postList: [{
ref: 'posts',
type: Schema.Types.ObjectId
}],
});
const UserModel = mongoose.model('users', UserSchema);
module.exports = UserModel;
// save post controller
exports.savePost = (request, response, next) => {
let { author, description, title } = request.body;
let post = new PostModel({ author, description, title }).save();
UserModel.findById(author)
.then((user) => {
user.postList.push(post);
// Still Fails
// How can i assign the post to the user ?
});
}
Is there any way of doing this other then push or populate ?
To solve this problem I prefer to use $push of mongo
UserModel.findOneAndUpdate({
_id: author.id,
{
$push: {
postList: post
}
}
});
You need to follow this process to save successfully
Save post if success then
Update user to push postId in postlist
can try this one
exports.savePost = (request, response, next) => {
let post = new PostModel(request.body)
post.save(function(err, data) {
if(err) {
//return error
}
// check by console your author is present or not
// let author in your req.body
let author = req.body.author
UserModel.findOneAndUpdate({_id: author},{$push: {postList: post._id}},{new:true} function(error, user) {
if(error) {
// return error
}
console.log(user)
// return success
})
});
}
exports.savePost = (request, response, next) => {
let { user, description, title } = request.body;
let post = new PostModel({ user, description, title });
post.save()
.catch((error) => {
if (error)
throw new Error(error);
})
.then((post) => {
UserModel.findOneAndUpdate({ _id: user }, {$push: { postList: post._id } })
.populate('postList')
.catch((error) => {
if (error)
throw new Error(error);
})
.then((user) => {
user.postList.forEach((item, postion) => {
console.log(`${item} -at ${postion} \n`);
});
});
});
}
This is what i did and it worked after all. I don't know if this is the correct solution but this is working.

Categories