I'm pretty new to node and mongoose, still learning a lot. Basically I am trying to create a forum page. I have a forumpost schema and I have recently added in a new field that I would like to show which user posted it. I have read other questions on this online and I was able to follow the code on there however mine is still not working. When i check my data in atlas it is still missing the new 'submitted by' field that I added. I have already deleted the 'collection' and have started over but it is still missing. Any help would be appreciated. Heres my models below as well as a screencap of how the data is being posted to the db.
**Post Form Schema**
const mongoose = require('mongoose');
const PostSchema = new mongoose.Schema({
title: {
type: String,
required: true
},
body: {
type: String,
required: true,
},
date: {
type: Date,
default: Date.now,
required: true,
},
submittedBy: { *(this is where I would like to get the user who submitted the form)*
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
},
extraInfo: {
type: String,
default: 'Other info goes here',
}
})
const Post = mongoose.model('Post', PostSchema);
module.exports = Post;
**Users Form Schema**
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
}
});
const User = mongoose.model('User', UserSchema);
module.exports = User;
EDIT: heres my newpost route
const express = require('express');
const Post = require('../models/post');
const router = express.Router();
const {ensureAuthenticated} = require("../config/auth.js");
router.get('/', ensureAuthenticated, (req, res) => {
res.render('newPost')
})
router.post('/', ensureAuthenticated, (req, res) => {
const post = new Post(req.body);
console.log(req.body)
post.save()
.then((result) => {
res.redirect('/dashboard')
})
.catch((err) => {
console.log(err)
})
})
module.exports = router;
If I'm not mistaken, you validate if is authenticated with the "ensureAuthenticated" middleware (the user ID should be there) but when creating the "Post" you only do it with the body data.
It is something like this ( you should replace "userId" with your property name):
const post = new Post({ ...req.body, submittedBy: userId })
Related
i'm new to nodeJS. am trying a use post request to get the information the user post but am getting an error: TypeError: Cannot read properties of undefined (reading 'title'). please what am i doing wrong here.
here's my code
const express = require("express")
const app = express()
const mongoose = require("mongoose")
const Schema = mongoose.Schema;
const BlogPost = new Schema({
title:{
type: String,
required: true
},
content: {
type: String,
required: true
},
body: {
type: String,
required: true
}
})
const Blog = mongoose.model("Blog", BlogPost)
module.exports = Blog;
app.post("/blogs", (req, res) => {
const blogs = new Blog({
title:req.body.title,
content: req.body.content,
body: req.body.body,
})
blogs.save()
.then(result => console.log(result))
.catch(err => console.log(err))
})
To make this code to workable condition for that add app.use(express.json())
As we are accepting http request body value from postman , so that it will parse our http request into json payload,
Hence we can access the body parameters value by req.body..
const express = require("express")
const app = express()
const mongoose = require("mongoose")
const Schema = mongoose.Schema;
// 2 json parsing
app.use(express.json())
//as my mongoose version is latest one so in that strictQuery is not supported
mongoose.set('strictQuery', false);
const BlogPost = new Schema({
title: {
type: String,
required: true
},
content: {
type: String,
required: true
},
body: {
type: String,
required: true
}
})
const Blog = mongoose.model("Blog", BlogPost)
module.exports = Blog;
//3. connection bridge between applicaion and mongoose
const connectionUrl = 'mongodb://127.0.0.1:27017'
mongoose.connect(connectionUrl, {
useNewUrlParser: true
},()=>{
console.log('Connected to MongoDB')
})
//Middleware
app.post("/blogs", (req, res) => {
//console.log(req.body.title+ " "+ req.body.content+ " "+ req.body.body );
const blogs = new Blog({
title: req.body.title,
content: req.body.content,
body: req.body.body,
})
blogs.save()
.then(result => console.log(result))
.catch(err => console.log(err))
})
// 1.listen
app.listen(3033, () => {
console.log("Server is running on port 3033")
});
/**
* use post request to get the information
* the user post but getting an error:
* TypeError: Cannot read properties of undefined (reading 'title').
*/
enter image description here
enter image description here
You may not get title from req.body.title that's why when you save this doc to mongodb it will throw this error.
For solution, you must check req.body.title before saving data into mongodb,
Otherwise, simply REMOVE required: true from schema
title:{
type: String,
required: true
},
I created a comment feature in my node.js blog application. I am using Mongodb data base. Everything works fine in postman. I can create new comments. However, I am trying to replicate the same in my client side via React.js. I think I have exhausted all my ideas. Here is the error message I keep getting each time I try to create a new comment from client side: This error is console.log from the catch(err) in backend.
undefined
CastError: Cast to ObjectId failed for value ":id" (type string) at path "_id" for model "Post"
Post Model
//creating the user models for the database
const mongoose = require("mongoose"); //import mongoose
const Schema = mongoose.Schema;
const PostSchema = new mongoose.Schema(
{
title:{
type: String,
required: true,
unique: true,
},
description:{
type: String,
required: true,
},
postPhoto:{
type: String,
required:false,
},
username:{
type: Schema.Types.ObjectId,
ref: 'User'
},
categories:{
type: Array,
},
comments: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Comment',
unique: true,
}]
}, {timestamps: true},
);
//exporting this schema
module.exports = mongoose.model("Post", PostSchema); //the module name is "Post"
Comment Model
const mongoose = require("mongoose"); //import mongoose to be used
const Schema = mongoose.Schema;
const CommentSchema = new mongoose.Schema(
{
commentdescription:{
type: String,
required: true,
},
author:{
type: Schema.Types.ObjectId,
ref: 'User',
},
}, {timestamps: true}
);
//exporting this schema
module.exports = mongoose.model("Comment", CommentSchema); //the module name is "Post"
create new comment and push to post codes
//creating catergory logic
router.post("/posts/:id/comment", async (req, res) =>{
const newComment = new Comment(req.body);//we create a new comment for the database
try{
//we need to try and catch the new comment and save it
const currentPost = await Post.findByIdAndUpdate(req.params.id)//we need to find the post that has the comment via the id
currentPost.comments.push(newComment)//we need to push the comment into the post
await newComment.save();
await currentPost.save()//we saved the new post with the comment
res.status(200).json(currentPost)
}catch(err){
console.log(err)
res.status(500).json(err)
}
})
What I have written in React
const [commentdescription, setCommentDescription] = useState('')
const [postId, setPostId] = useState()
//function to create comment
const handleComment = async ()=>{
const newComment = {
_id: postId,
author: user._id,
commentdescription,
};
try{
await axios.post("/posts/:id/comment", newComment);
}catch(err){
console.log(err)
}
}
Everything works fine in postman. I know I am missing something in react.
You need to pass actual ID to that POST request url.
await axios.post(`/posts/${postId}/comment`, newComment);
When POSTing to /posts/:id/comment, your parsed value (req.params.id) will literally be :id, which cannot be casted as ObjectId.
I am trying to build a simple blog with a commenting system with mongoose and express. There is no issse of creating and posting blogs here and each post can be displayed correctly. However, there are some issues associated with comments and each blog. The relationship between comments and blog post was established by applying mongoose.Schema.Types.ObjectId in the post Schema and the comments has been created to store array of comments ids. I think the schema structures are correct and there might be some problems in my code for routing and please help, thanks.
// Post Schema
const mongoose = require('mongoose');
const postSchema = new mongoose.Schema({
title: {
type: String,
trim: true,
required: true
},
text: {
type: String,
trim: true,
required: true
},
date: {
type: Date,
default: Date.now
},
comments: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Comment'
}]
})
postSchema.virtual('url').get(function(){
return '/post/' + this._id
})
module.exports = mongoose.model('Post', postSchema);
// Comment Schema
const mongoose = require('mongoose');
const commentSchema = new mongoose.Schema({
text: {
type: String,
trim: true,
required: true
},
date: {
type: Date,
default: Date.now
}
})
module.exports = mongoose.model('Comment', commentSchema);
// Router
const express = require('express');
const Post = require('../models/post');
const Comment = require('../models/comment');
const router = new express.Router();
// Get comments
router.get('/post/:id/comment', (req, res) => {
res.render('post-comment', {title: 'Post a comment'})
})
// Create and Post comments, this is where I think I made mistakes
router.post('/post/:id/comment', async (req, res) => {
const comment = new Comment({text: req.body.text});
const post = await Post.findById(req.params.id);
const savedPost = post.comments.push(comment);
savedPost.save(function(err, results){
if(err) {console.log(err)}
res.render('post_details', {title: 'Post details', comments:
results.comments})
} )
})
// Get each post details.
// Trying to display comments, but it is all empty and I realized
// the comments array is empty, I can see the comments create in
// mongodb database why is that?????
router.get('/post/:id', (req, res) => {
Post.findById(req.params.id)
.populate('comments')
.exec(function(err, results) {
if(err) {console.log(err)}
res.render('post_details', {title: 'Post details', post:
results, comments: results.comments})
})
})
router.get('/new', (req, res) => {
res.render('create-post', {title: 'Create a post'})
})
router.post('/new', (req, res) => {
const post = new Post({
title: req.body.title,
text: req.body.text
});
post.save(function(err) {
if(err) {console.log(err)}
res.redirect('/')
})
})
router.get('/', (req, res) => {
Post.find()
.exec(function(err, results) {
if(err) {console.log(err)}
res.render('posts', {title: 'All Posts', posts: results})
})
});
module.exports = router;
It has been a few days after I post this question and I have received no answer so far. But I have figure out why my code is not working and this post will try to answer this question I asked a few days ago and hope it can help some who is struggling the same issue.
My problems here are each comment created can not been pushed into the array of comments in Post, so the comments can not be displayed as the array is empty.
If you look at my code for Schema, you might realised that I made mistake for comment schema as I did not define a post key value pair, so the correct comment and post schema should be as below. The logic here is that each blog post can has multiple comments below, so the comments in post Scheme should be created as a array, but each comment can only below to one of the post, therefore the post key value pair in comment schema should be only in a object
const mongoose = require('mongoose');
const commentSchema = new mongoose.Schema({
text: {
type: String,
trim: true,
required: true
},
date: {
type: Date,
default: Date.now
},
// each comment can only relates to one blog, so it's not in array
post: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Post'
}
})
module.exports = mongoose.model('Comment', commentSchema);
and the post schema should be as below
const mongoose = require('mongoose');
const postSchema = new mongoose.Schema({
title: {
type: String,
trim: true,
required: true
},
text: {
type: String,
trim: true,
required: true
},
date: {
type: Date,
default: Date.now
},
// a blog post can have multiple comments, so it should be in a array.
// all comments info should be kept in this array of this blog post.
comments: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Comment'
}]
})
postSchema.virtual('url').get(function(){
return '/post/' + this._id
})
module.exports = mongoose.model('Post', postSchema);
Another important thing I did not do was, every time a comment was posted, this comment should be pushed into the post.comments array. I did not do this step, that's why my array was always empty and there is no comment to display. the code to correct this is go to file commentRouter.js (I have created both of postRouter.js and commentRouter.js), add code below
router.post('/post/:id/comment', async (req, res) => {
// find out which post you are commenting
const id = req.params.id;
// get the comment text and record post id
const comment = new Comment({
text: req.body.comment,
post: id
})
// save comment
await comment.save();
// get this particular post
const postRelated = await Post.findById(id);
// push the comment into the post.comments array
postRelated.comments.push(comment);
// save and redirect...
await postRelated.save(function(err) {
if(err) {console.log(err)}
res.redirect('/')
})
})
The above is how I fixed my error, hope it can help someone.
Thank you for reading
I have created post route to store posts in the database. It's a protected route so user can store post only after entering the login details. When I post in postman, I've seen that the user email is not returned in the object. Even in the mongodb collection, I don't see the email associated with the post. How do I include the email as well with the post object. I don't want the user to enter the email again and again when posting because they have already logged in. So I kinda want to store the email automatically with the post. Hope I make sense. Can someone help me with this?
Right now the object is kinda stored like this in the posts collection in mongodb
_id: ObjectId("5f1a99d3ea3ac2afe5"),
text: "It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. ",
user:ObjectId("5f1a99d3eac2c82afe5"),
age:20,
country:"India",
gender:"male",
date:2020-07-24T08:23:35.349+00:00,
__v:0
I want the email too in the above object.
Post model
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const PostSchema = new Schema ({
user: {
type: Schema.Types.ObjectId,
ref: 'users'
},
text: {
type: String,
required: true
},
name: {
type: String
},
email: {
type: String
}
,
age: {
type: Number,
required: true
},
gender: {
type: String,
required: true
},
country: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
}
})
module.exports = Post = mongoose.model('post', PostSchema)
post route
const express = require('express');
const router = express.Router();
const auth = require('../../middleware/auth')
const { check, validationResult} = require('express-validator');
const User = require('../../models/User')
const Post = require('../../models/Post')
router.post('/', [auth, [
check('text', 'Text is required').not().isEmpty()
]], async (req,res)=>{
const errors = validationResult(req);
if(!errors.isEmpty()){
return res.status(400).json({errors: errors.array()})
}
try {
const user = await (await User.findById(req.user.id)).isSelected('-password')
const newPost = new Post({
text: req.body.text,
name: user.name,
user: req.user.id,
age: req.body.age,
country: req.body.country,
gender: req.body.gender,
email: req.user.email // this email is not stored with the post and I want this to be automatically posted in the collection without the user having to type it again to save the post
})
const post = await newPost.save();
res.json(post);
} catch (err) {
console.error(err.message);
res.status(500).send('Server Error')
}
})
module.exports = router;
User model
const mongoose = require('mongoose');
const UserSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
}
})
module.exports = User = mongoose.model('user', UserSchema);
Change isSelected to select
const user = await (await User.findById(req.user.id)).isSelected(password')
What I potentially see the problem here is, once you have grabed the object of user, you're still referring to req.user.email instead of user.email.
If that does not solve your problem, try to console.log the user returned from after User.findById
Update:
You can see here that isSelected returns boolean. So you're essentialy getting true for having password field in user. Also instead of req.user.email use user.email
I think it's not populating categories couse when i try log 'category' in console i get
"ReferenceError: category is not defined". For me it is like in docs but as we see it's not. Is anyone can tell me what is wrong??
//model/Category.js
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const CatSchema = new Schema({
title: {
type: String,
required: true
},
body: {
type: String,
required: true
}
});
mongoose.model("categories", CatSchema, "categories");
model/Story.js
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const StorySchema = new Schema({
title: {
type: String,
required: true
},
body: {
type: String,
required: true
},
category: {
type: Schema.Types.ObjectId,
ref: "categories"
}
});
mongoose.model("stories", StorySchema, "stories");
routes/stories.js
const express = require('express');
const router = express.Router();
const mongoose = require('mongoose');
const Category = mongoose.model('categories');
const Story = mongoose.model('stories');
router.get('/add', (req, res) => {
Story.find()
.populate('category', 'title')
.then(stories => {
res.render('stories/add', {
stories: stories
});
});
});
Query.prototype.populate() returns a Query object on which you need to run .exec(), try this:
Story.find({})
.populate('category', 'title')
.exec()
.then(stories => {
res.render('stories/add', {
stories: stories
});
});
It was problem with that how I want to use it. Code is working