I'm developing a Node.js Express & MongoDB web application.
Actually, I'm trying to use the MVC feature. However, there is a problem with my code after that I added the update functionality to the model.
Indeed, when I try to update a post, my application crashes and I get the following error message:
TypeError: C:\Users\DELL\Desktop\node-com4muz-database-mvc\views\admin\posts-list.ejs:19
17| <ol id="posts-list">
18| <% for (const post of posts) { %>
>> 19| <li><%- include('includes/post-item', { post: post }) %></li>
20| <% } %>
21| </ol>
22| <% } %>
C:\Users\DELL\Desktop\node-com4muz-database-mvc\views\admin\includes\post-item.ejs:3
1| <article class="post-item">
2| <h2><%= post.title %></h2>
>> 3| <p class="post-item-author">Rédigé par <%= post.author.name %></p>
4| <p><%= post.summary %></p>
5| <div class="post-actions">
6| <form action="/blog/<%= post._id %>/delete" method="POST">
Cannot read properties of undefined (reading 'name')
at eval ("C:\\Users\\DELL\\Desktop\\node-com4muz-database-mvc\\views\\admin\\includes\\post-item.ejs":15:38)
at post-item (C:\Users\DELL\Desktop\node-com4muz-database-mvc\node_modules\ejs\lib\ejs.js:703:17)
at include (C:\Users\DELL\Desktop\node-com4muz-database-mvc\node_modules\ejs\lib\ejs.js:701:39)
at eval ("C:\\Users\\DELL\\Desktop\\node-com4muz-database-mvc\\views\\admin\\posts-list.ejs":29:17)
at posts-list (C:\Users\DELL\Desktop\node-com4muz-database-mvc\node_modules\ejs\lib\ejs.js:703:17)
at tryHandleCache (C:\Users\DELL\Desktop\node-com4muz-database-mvc\node_modules\ejs\lib\ejs.js:274:36)
at View.exports.renderFile [as engine] (C:\Users\DELL\Desktop\node-com4muz-database-mvc\node_modules\ejs\lib\ejs.js:491:10)
at View.render (C:\Users\DELL\Desktop\node-com4muz-database-mvc\node_modules\express\lib\view.js:135:8)
at tryRender (C:\Users\DELL\Desktop\node-com4muz-database-mvc\node_modules\express\lib\application.js:657:10)
at Function.render (C:\Users\DELL\Desktop\node-com4muz-database-mvc\node_modules\express\lib\application.js:609:3) {
path: 'C:\\Users\\DELL\\Desktop\\node-com4muz-database-mvc\\views\\admin\\posts-list.ejs'
}
Actually, before I changed the code in the model file (see below) and the route file (see below), the update functionality was working fine.
That's the reason why I think we should compare both codes, before and after adding the update functionality to the model, to find where is the issue.
Here is my code before adding the update functionality to the model:
routes\admin\blog.js:
const express = require('express');
const mongodb = require('mongodb');
const multer = require('multer');
const storageConfig = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'public/admin/images');
},
filename: function (req, file, cb) {
cb(null, Date.now() + '-' + file.originalname);
}
});
const db = require('../../data/database');
const Post = require('../../models/post');
const ObjectId = mongodb.ObjectId;
const upload = multer({ storage: storageConfig });
const router = express.Router();
router.get('/posts', async function (req, res) {
const posts = await db
.getDb()
.collection('posts')
.find({})
.project({ title: 1, summary: 1, 'author.name': 1 })
.toArray();
res.render('posts-list', { posts: posts });
});
router.get('/new-post', async function (req, res) {
const authors = await db.getDb().collection('authors').find().toArray();
res.render('create-post', { authors: authors });
});
router.post('/new-post', upload.single('image'), async function (req, res) {
const uploadedImageFile = req.file;
const authorId = new ObjectId(req.body.author);
const author = await db
.getDb()
.collection('authors')
.findOne({ _id: authorId });
const enteredTitle = req.body.title;
const enteredSummary = req.body.summary;
const enteredContent = req.body.content;
const date = new Date();
const selectedAuthor = {
author: {
id: authorId,
name: author.name,
email: author.email
}
};
const selectedImage = uploadedImageFile.path;
const post = new Post(
enteredTitle,
enteredSummary,
enteredContent,
date,
selectedAuthor.author,
selectedImage
);
await post.save();
res.redirect('/posts');
});
router.get('/blog/:id/edit', async function (req, res) {
const postId = req.params.id;
const post = await db
.getDb()
.collection('posts')
.findOne(
{ _id: new ObjectId(postId) },
{ title: 1, summary: 1, content: 1 }
);
if (!post) {
return res.status(404).render('404');
}
res.render('update-post', { post: post });
});
router.post('/blog/:id/edit', async function (req, res) {
const postId = new ObjectId(req.params.id);
const result = await db
.getDb()
.collection('posts')
.updateOne(
{ _id: postId },
{
$set: {
title: req.body.title,
summary: req.body.summary,
content: req.body.content
// date: new Date()
}
}
);
res.redirect('/posts');
});
router.post('/blog/:id/delete', async function (req, res) {
const postId = new ObjectId(req.params.id);
const result = await db
.getDb()
.collection('posts')
.deleteOne({ _id: postId });
res.redirect('/posts');
});
router.get('/admin', async function (req, res) {
if (!res.locals.isAuth) {
// if (!req.session.user)
return res.status(401).render('401');
}
if (!res.locals.isAdmin) {
return res.status(403).render('403');
}
res.render('admin');
});
module.exports = router;
models\post.js:
const db = require('../data/database');
class Post {
constructor(title, summary, content, date, author, image, id) {
this.title = title;
this.summary = summary;
this.content = content;
this.date = date;
this.author = author;
this.image = image;
this.id = id; // may be undefined
}
async save() {
const newPost = {
title: this.title,
summary: this.summary,
content: this.content,
date: this.date,
author: this.author,
imagePath: this.image
};
const result = await db.getDb().collection('posts').insertOne(newPost);
// console.log(result);
return result;
}
}
module.exports = Post;
And here is the code after adding the update functionality to the model:
routes\admin\blog.js:
const express = require('express');
const mongodb = require('mongodb');
const multer = require('multer');
const storageConfig = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'public/admin/images');
},
filename: function (req, file, cb) {
cb(null, Date.now() + '-' + file.originalname);
}
});
const db = require('../../data/database');
const Post = require('../../models/post');
const ObjectId = mongodb.ObjectId;
const upload = multer({ storage: storageConfig });
const router = express.Router();
router.get('/posts', async function (req, res) {
const posts = await db
.getDb()
.collection('posts')
.find({})
.project({ title: 1, summary: 1, content: 1, 'author.name': 1 })
.toArray();
res.render('posts-list', { posts: posts });
});
router.get('/new-post', async function (req, res) {
const authors = await db.getDb().collection('authors').find().toArray();
res.render('create-post', { authors: authors });
});
router.post('/new-post', upload.single('image'), async function (req, res) {
const uploadedImageFile = req.file;
const authorId = new ObjectId(req.body.author);
const author = await db
.getDb()
.collection('authors')
.findOne({ _id: authorId });
const enteredTitle = req.body.title;
const enteredSummary = req.body.summary;
const enteredContent = req.body.content;
const date = new Date();
const selectedAuthor = {
author: {
id: authorId,
name: author.name,
email: author.email
}
};
const selectedImage = uploadedImageFile.path;
const post = new Post(
enteredTitle,
enteredSummary,
enteredContent,
date,
selectedAuthor.author,
selectedImage
);
await post.save();
res.redirect('/posts');
});
router.get('/blog/:id/edit', async function (req, res) {
const postId = req.params.id;
const post = await db
.getDb()
.collection('posts')
.findOne(
{ _id: new ObjectId(postId) },
{ title: 1, summary: 1, content: 1 }
);
if (!post) {
return res.status(404).render('404');
}
res.render('update-post', { post: post });
});
router.post('/blog/:id/edit', async function (req, res) {
const enteredTitle = req.body.title;
const enteredSummary = req.body.summary;
const enteredContent = req.body.content;
const post = new Post(
enteredTitle,
enteredSummary,
enteredContent,
req.params.id
);
await post.save();
res.redirect('/posts');
});
router.post('/blog/:id/delete', async function (req, res) {
const postId = new ObjectId(req.params.id);
const result = await db
.getDb()
.collection('posts')
.deleteOne({ _id: postId });
res.redirect('/posts');
});
router.get('/admin', async function (req, res) {
if (!res.locals.isAuth) {
// if (!req.session.user)
return res.status(401).render('401');
}
if (!res.locals.isAdmin) {
return res.status(403).render('403');
}
res.render('admin');
});
module.exports = router;
models\post.js:
const mongodb = require('mongodb');
const db = require('../data/database');
const ObjectId = mongodb.ObjectId;
class Post {
constructor(title, summary, content, date, author, image, id) {
this.title = title;
this.summary = summary;
this.content = content;
this.date = date;
this.author = author;
this.image = image;
if (id) {
this.id = new ObjectId(id);
}
}
async save() {
let result;
if (this.id) {
result = await db
.getDb()
.collection('posts')
.updateOne(
{ _id: postId },
{
$set: {
title: this.id,
summary: this.summary,
content: this.content
}
}
);
} else {
result = await db.getDb().collection('posts').insertOne({
title: this.title,
summary: this.summary,
content: this.content,
date: this.date,
author: this.author,
imagePath: this.image
});
}
return result;
}
}
module.exports = Post;
You're passing id as a wrong argument to when creating post object under /blog/:id/edit route, it doesn't get picked up, so it saves the post, instead of updating it, making other properties undefined.
Try this, pass 3 undefined arguments before id, which is passed as the last argument under /blog/:id/edit route:
const post = new Post(
enteredTitle,
enteredSummary,
enteredContent,
...[,,,], // pass 3 undefined arguments
req.params.id
);
await post.save();
Related
axios script.js file
const creatClient = async (client) => {
try {
const res = await axios({
method: 'POST',
withCredentials: true,
url: '/[url]',
data: client,
}).then(location.assign('/[newUrl]'));
} catch (error) {
console.log(error);
}
};
submitbtn.addEventListener('click', (e) => {
e.preventDefault;
const name = document.getElementById('name').value;
const phone = document.getElementById('phone').value;
const createdATT = new Date(document.getElementById('date').value);
const followUp = new Date(document.getElementById('date2').value);
const images = document.getElementById('img').value;
const insurance = document.getElementById('insurance').value;
const client = { name, phone, insurance, images, createdATT, followUp };
console.log(client);
client ? creatClient(...client) : console.log('no object created');
});
controller file
the console log for req.body [Object: null prototype] {*** the object ***}
const multer = require('multer');
const Client = require('../models/clientModel');
const multerStorage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'public/img');
},
filename: (req, file, cb) => {
const ext = file.mimetype.split('/')[1];
cb(null, `user-${Date.now()}.${ext}`);
},
});
const multerFilter = (req, file, cb) => {
if (file.mimetype.startsWith('image')) {
cb(null, true);
} else {
cd(console.log('select image'), false);
}
};
const upload = multer({
storage: multerStorage,
fileFilter: multerFilter,
});
exports.uploadImages = upload.single('images');
//
exports.createClients = async (req, res, next) => {
try {
if (req.file) req.body.images = req.file.filename;
const newClient = { ...req.body };
await Client.create(req.body).then(
res.status(200).json({
status: 'success',
newClient,
})
);
} catch (err) {
console.log(err);
}
};
also with postman sending request give success response with no errors
i've tried location.replace() but also it didn't work for me
and is there another trick from server to get to the desired location out from client side
then accepts a callback as a parameter.
then(() => location.assign('/[newUrl]'))
I can't connect to MongoDB database, yet I tried everything ! I have successfully replaced the password.
const mongoose = require("mongoose");
mongoose
.connect(
"mongodb+srv://vibess:0KksWIBp6slcBLm0#cluster0.iuvoi.mongodb.net/?retryWrites=true&w=majority",
{
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
}
)
.then(() => console.log("Connected !!"))
.catch(() => console.log("Not connected!"));
Here is the Database
MangoDB
You will need another function to complete the operation. the function is usually called run then you need to write all the operations of your server in the scope of this function. here is an example of a server of mine Also you need to declare the name of your database before text retrywrites=true
//connect to mongodb
const uri = `mongodb+srv://${process.env.DB_USER}:${process.env.DB_PASS}#cluster0.qtoag.mongodb.net/Teletale?retryWrites=true&w=majority`;
const client = new MongoClient(uri, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
async function run() {
try {
await client.connect((err) => {
const db = client.db("Teletale");
const djiPackages = db.collection("Devices");
const bookingsCollection = db.collection("bookings");
const testimonialCollection = db.collection("testimonials");
const usersCollection = db.collection("users");
// ==============GET API ====================
//GET API
app.get("/", (req, res) => {
res.send("Welcome to Teletale");
});
//GET API (dji Package)
app.get("/Devices", async (req, res) => {
const result = await djiPackages.find({}).toArray();
res.send(result);
});
//GET API (users)
app.get("/users", async (req, res) => {
const result = await usersCollection.find({}).toArray();
res.send(result);
});
// verify admin data form database
app.get("/users/:email", async (req, res) => {
const email = req.params.email;
const query = { email: email };
const user = await usersCollection.findOne(query);
let isAdmin = false;
if (user?.role === "admin") {
isAdmin = true;
}
// localhost:5000/users/admin#admin.com will show true
res.json({ admin: isAdmin });
});
//GET API (Bookings)
app.get("/bookings", async (req, res) => {
let query = {};
const email = req.query.email;
if (email) {
query = { email: email };
}
const result = await bookingsCollection.find(query).toArray();
res.send(result);
});
//GET Dynamic (Bookings)
app.get("/bookings/:id", async (req, res) => {
const id = req.params.id;
const query = { _id: ObjectId(id) };
const result = await bookingsCollection.findOne(query);
res.send(result);
});
//GET Dynamic (products)
app.get("/Devices/:id", async (req, res) => {
const id = req.params.id;
const query = { _id: ObjectId(id) };
const result = await djiPackages.findOne(query);
res.send(result);
});
//GET (testimonials)
app.get("/testimonials", async (req, res) => {
const result = await testimonialCollection.find({}).toArray();
res.send(result);
});
// ==========================POST API=========================
//POST API (dji Package)
app.post("/Devices", async (req, res) => {
const newTours = req.body;
const result = await djiPackages.insertOne(newTours);
res.send(result);
});
//POST API (users)
app.post("/users", async (req, res) => {
const user = req.body;
const result = await usersCollection.insertOne(user);
console.log(result);
res.send(result);
});
//POST API (Bookings )
app.post("/bookings", async (req, res) => {
const newBooking = req.body;
const result = await bookingsCollection.insertOne(newBooking);
res.send(result);
});
//POST API (Testimonials )
app.post("/testimonials", async (req, res) => {
const newBooking = req.body;
// console.log(newBooking);
const result = await testimonialCollection.insertOne(newBooking);
res.send(result);
});
// ======================DELETE API ========================
//DELETE API(Bookings)
app.delete("/bookings/:id", async (req, res) => {
const id = req.params.id;
const query = { _id: ObjectId(id) };
const result = await bookingsCollection.deleteOne(query);
res.send(result);
});
//DELETE API(drone)
app.delete("/Devices/:id", async (req, res) => {
const id = req.params.id;
const query = { _id: ObjectId(id) };
const result = await djiPackages.deleteOne(query);
res.send(result);
});
// =================Update API====================
app.put("/bookings/:id", async (req, res) => {
const id = req.params.id;
const newStatus = req.body;
const query = { _id: ObjectId(id) };
const options = { upsert: true };
const updateDoc = {
$set: {
data: newStatus.newData,
},
};
const result = await bookingsCollection.updateOne(
query,
updateDoc,
options
);
res.send(result);
});
//upsert Google user data
app.put("/users", async (req, res) => {
const user = req.body;
const filter = { email: user.email };
const options = { upsert: true };
const updateDoc = { $set: user };
const result = await usersCollection.updateOne(
filter,
updateDoc,
options
);
res.json(result);
});
// add admin role
app.put("/users/admin", async (req, res) => {
const user = req.body;
const filter = { email: user.email };
const updateDoc = { $set: { role: "admin" } };
const result = await usersCollection.updateOne(filter, updateDoc);
res.json(result);
});
});
} finally {
// await client.close();
}
}
I am trying to use 'POST' to create a new movie data with using MongoDB. but when I use 'postman' to post my data it kepts saying like this
{"message":"Operation movies.insertOne() buffering timed out after 10000ms"}
I will post three javascript files please let me know about my problem.
First : index
Second : movie(module)
Third : routes
const router = express.Router();
const Movie = require('./models/movie');
//Fetch all movies
router.get("/movies", async (req, res) => {
try {
const movies = await Movie.find();
res.send(movies)
} catch(err) {
return res.status(500).json({message: err.message});
}
})
// Add movie
router.post("/movies", async (req, res) => {
const movie = new Movie({
title: req.body.title,
director: req.body.director,
year: req.body.year
});
try {
const newMovie = await movie.save();
res.status(201).json({ newMovie });
} catch(err) {
return res.status(500).json({ message: err.message });
}
})
module.exports = router;
//from here is Movie.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var MovieSchema = new Schema(
{
title: {type: String, required: true, maxlength: 150},
director: {type: String, required: true, maxlength: 200},
year: {type: Number, required: true}
}
);
//Export model
module.exports = mongoose.model('Movie', MovieSchema);
// from here is routes.js
const express = require('express');
const router = express.Router();
const Movie = require('./models/movie');
//Fetch all movies
router.get("/movies", async (req, res) => {
try {
const movies = await Movie.find();
res.send(movies)
} catch(err) {
return res.status(500).json({message: err.message});
}
})
// Add movie
router.post("/movies", async (req, res) => {
const movie = new Movie({
title: req.body.title,
director: req.body.director,
year: req.body.year
});
try {
const newMovie = await movie.save();
res.status(201).json({ newMovie });
} catch(err) {
return res.status(500).json({ message: err.message });
}
})
enter code here
module.exports = router;
I feel this is so hard to understand lol
Try to refactor your code like this:
router.post("/movies", async (req, res) => {
try {
let newMovie = await Movie.create(req.body);
res.status(201).json(newMovie);
} catch(err) {
return res.status(500).json({ message: err.message });
}
})
In the provided code I don't see you that you are connecting to the database using mongoose.connect for example:
mongoose.connect('mongodb://localhost/test', {useNewUrlParser: true, useUnifiedTopology: true});
Link for the docs.
logger.js
const { createLogger, format, transports } = require('winston');
class Logger {
constructor() {
this.logger = createLogger({
transports: new transports.File({
level: 'error',
filename: 'error.log',
format: format.json(),
``
}),
});
}
logError(err) {
this.logger.error(err);
}
}
module.exports = Logger;
As you can see I am using prototype method.So that I don't have to use new method to create object. I have attached all the 3 files. Just want to know how can I use logger in user.js file without using new keyword in logger.js file.
user.js
exports.postUser = async (req, res, next) => {
try {
console.log(`name:${JSON.stringify(req.body)}`)
throw new Error('this is an error on the post');
let user = new User({
name: req.body.name,
age: parseInt(req.body.age),
city: req.body.city,
status: 'ACTIVE'
})
let savedPost = await user.save();
console.log(`savedPost':${JSON.stringify(savedPost)}`);
res.json(savedPost);
} catch (err) {
console.log(`err:${err.toString()}`)
res.status(500).json({ message: err })
here I want to call the logger .So that it can return method, err, date.
logger.prototype.logError = function() {
return (`function:controller.postUser, error:${err.toString()} -method: ${req.method}- body:${JSON.stringify(req.body)} date:${new Date(Date.now()).toUTCString()}`)
}
};
routes.js
const router = require('express').Router();
const { json } = require('body-parser');
const User = require('../models/user');
const controller = require('../controllers/user');
const middle = require('../middleware/middleware')
/*routes*/
router.get('/', controller.getUser);
router.post('/', middle.verify, controller.postUser);
router.put('/:userId', controller.updateUser)
router.delete('/:userId', controller.deleteUser);
module.exports = router
Well basically when I POST the create post controller, the post shows up once, the problem is when I GET the same page it shows up twice?
How can I solve this to make so that if the post already exists, then it should not show another time even tho I GET the page?
My goal is that altho the user post or gets the page, it should still be showing once if it exists.
admin controller
const path = require('path');
const bcrypt = require("bcryptjs");
const mongoose = require("mongoose");
const _ = require("lodash");
const {
User,
Post,
} = require("../models/model");
exports.getMyPostsPage = (req, res) => {
res.render("admin/myposts", {
path: "/myposts",
pageTitle: "My Posts",
})
}
exports.getCreatepostPage = (req, res) => {
res.render("admin/createpost", {
path: "/create",
pageTitle: "Create",
});
}
exports.getPostsPage = async (req, res) => {
try {
const posts = await Post.find({})
res.render("admin/posts", {
path: "/posts",
pageTitle: "Posts",
posts: posts
});
} catch (err) {
console.log(err);
}
}
exports.postCreatePost = async (req, res) => {
const {
title,
description,
context
} = req.body;
const post = new Post({
title,
description,
context,
author: req.user
});
try {
const savedPost = await post.save()
const usersPost = await req.user.posts.push(post);
console.log(req.user.posts)
const posts = await Post.find({})
res.render("admin/posts", {
pageTitle: "Posts",
path: "/posts",
posts: posts
})
} catch (err) {
console.log(err);
}
}
posts.ejs
<%- include("../includes/head.ejs") %>
<link rel="stylesheet" href="/css/admin.css">
</head>
<body>
<%- include("../includes/navigation.ejs", ) %>
<% for (const post of posts) { %>
<div class="posts">
<form action="/post" method="POST">
<h1><%=post.title%></h1>
<p><%=post.description%></p>
<input type="hidden" name="_csrf" value="<%= csrfToken %>">
<button type="submit">
See Post
</button>
</form>
</div>
<% } %>
<%- include("../includes/footer.ejs") %>
const mongoose = require("mongoose"),
Schema = mongoose.Schema,
bcrypt = require("bcryptjs");
const postSchema = new Schema({
title: String,
description: String,
context: String,
author: {
type: Schema.Types.ObjectId,
}
});
const userSchema = new Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
},
password: {
type: String,
required: true
},
posts: [postSchema]
});
userSchema.pre("save", async function save(next) {
const user = this;
if (!user.isModified("password")) return next();
const hashedPassword = await bcrypt.hash(user.password, 10);
user.password = hashedPassword;
next();
});
const Post = mongoose.model("Post", postSchema);
const User = mongoose.model("User", userSchema);
module.exports = {
User,
Post
}
I had an issue when it came to using the f5 button, it repeated the POST part, but it is fine now, it was actually not a real issue just a bug so to speak.