How to link documents on mongoose - express

I am new to express development and i am trying to build a blog. I have built two models, one for posts and one for uses. On users schema i have an attribute posts to save the post when a user creates a post. On the controller, before i create a post first i am taking the user's id from the req.params and after that i retrive the user by findbyid function and try to save the post on user's posts attribute, but with no succeful.
const mongoose = require("mongoose");
UserSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
},
posts: [{type: mongoose.Schema.Types.ObjectId, ref: "Post"}]
})
module.exports = mongoose.model("User", UserSchema);
const Post = require("../model/post");
const User = require("../model/user");
module.exports = {
new: (req, res) => {
res.render("new_post");
},
post_new: (req, res) => {
const title = req.body.title;
const article = req.body.article;
const id = req.params.id;
const post = new Post({
title: title,
article: article,
})
User.findById(id)
.then(user => {
user.posts.push(post);
})
//post.created_by.push(id);
post.save()
.then(result => {
console.log("Post has created");
res.redirect("/");
});
}
};

I see a few problems.
You user schema should not have array of posts. Instead, your post schema should have a field named user/userId to store user ID.
Example:
const PostSchema = new mongoose.Schema({
title: { type: String },
....,
userId: {type: mongoose.Schema.Types.ObjectId, ref: "User"}
});
Now your post_new function should be like this.
post_new: async (req, res) => {
const title = req.body.title;
const article = req.body.article;
const id = req.params.id;
const post = await Post.create({
title: title,
article: article,
userId: id
});
console.log("Post has created");
res.redirect("/");
}
If you want to stick with your way then the create_new function should be like this.
post_new: async (req, res) => {
const title = req.body.title;
const article = req.body.article;
const id = req.params.id;
const post = new Post({
title: title,
article: article,
});
const {_id} = await post.save();
const user = await User.findById(id);
user.posts.push(_id);
await user.save();
console.log("Post has created");
res.redirect("/");
}

Related

how to handle image upload in the mern stack using multer?

Im creating a MERN stack application and in the react front end, i intend to have a form to add a product, the form is going to have a lot of inputs including an image upload option. I want to know how to handle the image upload from the express side using Multer. i have used their documentation but im not sure whether the code i wrote is correct. I also haven't created the front end yet, so i am currently using postman to test the api. How do i test whether the image upload functionality is working using postman? I would be posting the code i have written so far for context.
Product model:
const mongoose = require('mongoose')
const ProductSchema = new mongoose.Schema({
name:{
type: String,
required: [true, 'please provide a product name'],
maxlength: 20,
minlength: 3
},
category: {
type: String,
required: [true, 'please provide a category'],
maxlength: 20,
minlength: 3
},
quantity: {
type: Number,
required: [true, 'please provide the quantity']
},
price: {
type: Number,
required: [true, 'please provide the price']
},
description: {
type: String,
required: [true, 'please provide the description'],
trim: true
},
image: {
type: String
},
createdBy: {
type: mongoose.Types.ObjectId,
ref: 'User',
required: [true, 'Please provide the user'],
}, },
{ timestamps: true } )
module.exports = mongoose.model('Product', ProductSchema)
file upload.js:
const multer = require('multer')
const { v4: uuidv4 } = require('uuid')
const storage = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, '../uploads')
},
filename: function(req, file, cb){
cb(null, uuidv4() + '-' + Date.now() + path.extname(file.originalname) )
}
})
const fileFilter = (req, file, cb) => {
const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png']
if(allowedTypes.includes(file.mimetype)) {
cb(null, true)
}else(
cb(null, false)
)
}
const upload = multer({storage, fileFilter})
module.exports = upload
Product router:
const express = require('express')
const router = express.Router()
const upload = require('../utils/fileUpload')
const {getAllProducts, createProduct, getProduct, updateProduct, deleteProduct} = require('../controllers/products')
router.route('/').post(upload.single('image'), createProduct).get(getAllProducts)
router.route('/:id').get(getProduct).patch(updateProduct).delete(deleteProduct)
module.exports = router
Product controller:
const Product = require('../models/Product')
const { StatusCodes } = require('http-status-codes')
const { BadRequestError, NotFoundError } = require('../errors')
const createProduct = async (req, res) => {
req.body.createdBy = req.user.userId
const product = await Product.create({...req.body, image: req.file})
res.send('create Product')
}
const getAllProducts = async (req, res) => {
res.send('get All products')
}
const getProduct = async (req, res) => {
res.send('get product')
}
const updateProduct = async (req, res) => {
res.send('update product')
}
const deleteProduct = async (req, res) => {
res.send('delete product')
}
module.exports = {
getAllProducts, createProduct, getProduct, updateProduct, deleteProduct
}
You can change the parameter type to file from postman to try uploading files when sending a request :

How to use Mongoose Populate between 2 different Schema's

I have 2 schema's, Categories and Cards. Each Category has an array of cards, and I want to populate that array with values , but I am unsure how to go about this as the mongoose documentation is somewhat confusing to understand.
// Schemas in seperate files
// Category Schema
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const categorySchema = new Schema({
title: {
type: String,
trim: true,
max: 30,
},
cards: [{ type: Schema.Types.ObjectId, ref: "categoryCard" }],
});
module.exports = mongoose.model("category", categorySchema);
// Category Card Schema
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const categoryCardSchema = new Schema({
category: {
type: String,
trim: true,
},
name: {
type: String,
trim: true,
},
post: {
type: String,
required: true,
trim: true,
},
});
module.exports = mongoose.model("categoryCard", categoryCardSchema);
// Below is the express router file . I want users to be able to create cards for different categories , after the category is already created. It worked in postman, but it doesn't work on the front end for some reason.
router.route("/createCard").post((req, res) => {
const { title, name, post } = req.body;
newCard = new categoryCard({
category: title,
name,
post,
});
newCard.save();
category.findOne({ title }).exec((err, item) => {
if (!err) {
item.cards.push(newCard._id);
item.save();
res.send(item);
} else {
res.send(err);
}
});
});
You can Follow this code...
let categorys= await category.findOne({ title }).populate("cards")

findOne is not a function

I am trying to create a model using Sequelize and mysql db.I am trying to post to '/students/register' it keeps giving me an error saying findOne is not a function. I tried requiring my sql but it's not working ..I also tried a different function like findAll and still not working.what seems to be the problem
const Sequelize = require('sequelize');
module.exports = function (sequelize, Sequelize) {
const Stundet = sequelize.define(
'student', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
name: {
type: Sequelize.STRING
},
email: {
type: Sequelize.STRING
},
password: {
type: Sequelize.STRING
},
created: {
type: Sequelize.DATE,
defaultValue: Sequelize.NOW
}
}, {
timestamps: false
});
module.exports = Stundet;
}
routes
const Student_Info = require("../models/students")
student.post('/register', (req, res) => {
const dataToday = new Date()
const studentData = {
name: req.body.name,
email: req.body.email,
password: req.body.password,
created: dataToday
}
Student_Info.findOne({
where: {
email: req.body.email
}
})
.then(student => {
if (!student) {
bcrypt.hash(req.body.password, 10, (err, hash) => {
studentData.password = hash
Student_Info.create(studentData)
.then(student => {
res.json({
status: student.email + 'registered'
})
})
.catch(err => {
res.send('error' + err)
})
})
} else {
res.json({
error: 'Student already registered'
})
}
})
.catch(err => {
res.send('error' + err)
})
})
module.exports = student;
When you use module.exports, you should return Stundet. You already export the whole function. And I think you should pass DataTypes instead of Sequelize.
Something like this:
module.exports = function (sequelize, DataTypes) {
const Stundet = sequelize.define(
//...
return Stundet;
}
So in your route in order to use your model:
const Sequelize = require('sequelize');
const DataTypes = sequelize.DataTypes;
let sequelize = new Sequelize(...);
const Student = require('../models/students')(sequelize, DataTypes);
I suspect that your Student_Info is null. Does you application successfully connect to the database? It helps to log... e.g.
sequelizeDB
.authenticate()
.then(() => {
console.log('Yes! DB Connection);
...
})
.catch(err => {
console.error('No! Unable to connect to DB', err);
});
... and IMHO the code reads better when you name the DB instance something other than "sequelize".

Create a post route that adds references to its relations

I've got the basic MERN Stack app running I can, GET, POST and Delete Topics and Post separately, they have relationship in Schema but I don't understand how to route it properly to incorporate the relations when POSTing.
const express = require('express');
const router = express.Router();
const Post = require("../../models/Post");
const Topic = require('../../models/Topic');
router.post("/", (req,res) => {
//gets topic id from param
const {topic} = req.params;
//creating a new post
const newPost = new Post({
post: req.body.post,
description: req.body.description,
topic_id: req.body.topic_id
});
//get topic by id
const topic_obj = Topic.findById(topic);
//add posts to topic_object
topic_obj.posts.push(newPost);
//and save
topic_obj.save()
.then(newPost.save()
.then(post => res.json(post)).catch(err => console.log(err)));
});
module.exports = router;
I Don't know if you need more information
I pushed it to GitHub for some helpL https://github.com/wolffles/bloccit-node/blob/react/routes/api/posts.js
Accoarding to TopicSchema:
const TopicSchema = new Schema({
topic: {
type: String,
required: true
},
description: {
type: String,
required: true
},
posts: [
{
type: Schema.Types.ObjectId,
ref: 'post'
}
],
date: {
type: Date,
default: Date.now
}});
To incorporate relation between Topic and Post you need to add _id property of newly created Post object to posts field in Topic object that this newly created Post represents.
topic_obj.posts.push(newPost._id);
EDIT:
In your code there are some issues that an cause errors.
const {topic} = req.params; You should validate whether topic is a valid ObjectId
You should also validate request body and check if description and post has required format.
You dupicate Topic id by passing it in request body and also asa request param.
const topic_obj = Topic.findById(topic); This is an async operation, it returns Promise not result of query.
Code should look like (it is not tested):
const express = require("express");
const router = express.Router();
const Post = require("../../models/Post");
const Topic = require("../../models/Topic");
router.post("/:topicId", async (req, res) => {
const { topicId } = req.params;
//TODO: validate if topic is a valid ObjectID
const validTopicId = validateTopicId(topicId);
if (!validTopicId) return res.status(400).send("Invalid param.");
//TODO: validate req.body.description and req.body.post
const validBody = validateBody(req.body);
if (!validBody) return res.status(400).send("Invalid data.");
const newPost = new Post({
post: req.body.post,
description: req.body.description,
topic_id: topicId
});
const topic = await Topic.findById(topicId);
if (!topic) return res.status(400).send("Invalid param.");
topic.posts.push(newPost._id);
await topic.save();
await newPost.save();
res.send();
});
module.exports = router;

comments not displaying on express app

I was watching a tutorial on a creating a restful app. I tried to my own project that was a little different from the tutorial and I am now stuck. I am trying to associate my Comments collection with Blog collection but data is not showing on my show page. I am having a hard time figuring out why my comments are not displaying on my blog post.
app.js file
var bodyParser = require('body-parser'),
methodOverride = require('method-override'),
expressSanitizer = require("express-sanitizer"),
mongoose = require('mongoose'),
express = require('express'),
app = express(),
Blog = require("./models/blog"),
Comment = require('./models/comment'),
seedDB = require("./seeds");
seedDB();
//APP CONFIG
mongoose.connect("mongodb://localhost:27017/blogApp", { useNewUrlParser: true
);
app.set("view engine", "ejs");
app.use(express.static("public"));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(expressSanitizer());
app.use(methodOverride("_method"));
// SHOW Route
app.get("/blog/:id", function (req, res) {
Blog.findById(req.params.id).populate("Comments").exec(function (err, foundBlog) {
if (err) {
console.log(err);
res.redirect("/blog");
} else {
console.log(foundBlog);
res.render("show", { blog: foundBlog });
}
});
});
Seed file
var mongoose = require("mongoose")
var Blog = require("./models/blog");
var Comment = require("./models/comment")
function seedDB() {
Blog.remove({}, function (err) {
if (err) {
console.log(err);
} else {
console.log("Removed Blogs");
}
data.forEach(function (seed) {
Blog.create(seed, function (err, blog) {
if (err) {
console.log(err)
} else {
console.log("blog created");
Comment.create(
{
text: "That was one great blog post",
author: "Homer"
}, function (err, comment) {
if (err) {
console.log(err)
} else {
blog.comments.push(comment);
comment.save();
console.log("created new comment");
}
}
)
}
})
});
});
}
comment model
var mongoose = require("mongoose");
var commentSchema = new mongoose.Schema({
text: String,
author: String
})
var Comment = mongoose.model("Comment", commentSchema);
module.exports = Comment
blog model
var blogSchema = new mongoose.Schema({
title: String,
image: String,
body: String,
created: { type: Date, default: Date.now },
comments: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Comment"
}
]
})
var Blog = mongoose.model("Blog", blogSchema);
module.exports = Blog
On my show.ejs page here is my code to try and display the comment but nothing populates
<% blog.comments.forEach(function(comment){ %>
<p><strong><%= comment.author %></strong> - <%= comment.text %> </p>
<% }) %>
here is my result from console.log in show route
{ comments: [],
_id: 5b666ed86feec81af8651b9b,
title: 'Test Blog 2',
image: 'https://images.unsplash.com/photo-1499938971550-7ad287075e0d?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=d787d5e47840a5a0a6ff7574c90a02d7&auto=format&fit=crop&w=500&q=60',
body: 'Hello this is a blog post',
created: 2018-08-05T03:28:24.404Z,
__v: 0 }
Please help me figure out why comments are not showing
I believe the issue is with this line
Blog.findById(req.params.id).populate("Comments").exec(function (err, foundBlog)
instead of populate("Comments") it should be populate("comments")
Try to execute once with the above change.