Check if document exists in collection with Mongoose Model - javascript

I want to to check if email already exists in 'users' collection:
I have this model:
const isEmailExists = async (value) => {
const res = await User.countDocuments({ email: value });
return res > 0;
}
const User = mongoose.model('User', {
email: {
type: String,
required: true,
validate(value) {
isEmailExists(value).then(res => {
if (res) {
throw new Error('Email already exists');
}
})
}
}
});
And I use post method with express router:
router
.route('/register')
.get((req, res) => {
res.sendFile(publicDirPath + '/auth/register.html');
})
.post(async (req, res) => {
const user = new User(req.body);
try {
const saveUser = await user.save();
res.send(saveUser);
} catch (error) {
res.send(error);
}
});
For some reason, it does not work and the user is been added anyway..
What am i doing wrong ?

If you want to check if one document with a certain entry/value exists you can do this :
function emailExists(value) {
User.findOne({email: value}).then((err, user) => !!user)
}

Related

Getting erros using passport-google-oauth20 InternalOAuthError: Failed to fetch user profile and Cannot set headers after they are sent to the client

I'm using passport strategies for different socialMedia logins and getting the following two errors
InternalOAuthError: Failed to fetch user profile
Cannot set headers after they are sent to the client
I have doubt there somewhere I have returned a callback or response so getting 2nd error but for 1st don't know reasons scope seems to be correct!
strategy code
passport.use(new GoogleStrategy({
clientID: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_SECRET_KEY,
callbackURL: GOOGLE_CALLBACK_URL
}, async (acessToken, refreshToken, profile, done) => {
await User.findOne({ email: profile._json.email }, async (err, user) => {
if (err) {
console.log("passport.config --> err", err);
done(err, null);
} else if (user) {
if (user.socialType !== "GOOGLE" || user.socialType === null)
done(`LOGIN_CREDENTIALS_WITH_${(user.socialType || "PASSWORD").toUpperCase()}`, false);
else {
done(null, user);
}
} else {
// console.log(profile);
const user = {
email: profile._json.email,
socialId: profile.id,
socialType: "GOOGLE",
firstName: profile.name.givenName,
lastName: profile.name.familyName,
isActive: profile._json.email_verified,
isVerified: profile._json.email_verified,
socialImageUrl: profile._json.picture,
userType: "CUSTOMER"
};
const newUser = new User({ ...user });
const newUserData = await newUser.save();
done(null, newUserData);
}
});
}));
route code:
router.get('/auth/:socialType', customerCtrl.socialTypeLogin);
router.get('/auth/:socialType/callback', customerCtrl.socialTypeLoginCallback);
controller code:
const socialTypeLogin = async (req, res) => {
await customerService.socialTypeLogin(req, res);
};
const socialTypeLoginCallback = async (req,res) => {
await customerService.socialTypeLoginCallback(req,res);
};
service code:
const socialTypeLogin = async (req, res) => {
try {
const socialType = (req.params.socialType || '').toLowerCase();
const GOOGLE_SCOPE = ['email', 'profile'];
const FACEBOOK_SCOPE = ['email'];
let scope = [];
if (socialType === 'google') {
scope = GOOGLE_SCOPE;
} else if (socialType === 'facebook') {
scope = FACEBOOK_SCOPE;
}
let oauthOptions = { scope: scope};
const { returnUrl } = req.query;
if(returnUrl && returnUrl.trim().length !== 0) {
oauthOptions['state'] =JSON.stringify({ returnUrl: returnUrl });
}
passport.authenticate(socialType, oauthOptions)(req, res);
}
catch (error) {
}
}
/**
* #param {string} socialType
*/
const socialTypeLoginCallback = async (req, res) => {
const socialType = (req.params.socialType || '').toLowerCase();
// return new Promise((resolve, reject) => {
try {
passport.authenticate(socialType, async (err, user) => {
let webappRedirectURL = WEBAPP_LOGIN_URL;
try {
const state = req.query.state;
if(state) {
const stateObj = JSON.parse(state);
webappRedirectURL = stateObj.returnUrl;
}
} catch (err1) {
console.log("customer.service --> parsing error",err1);
}
if (err || !user) {
console.log("customer.service --> !user",err);
res.render('oauth-redirect', {
webappRedirectURL: webappRedirectURL,
success: false,
error: err,
timerCounter: 5,
accessToken: undefined
});
}
else {
console.log("customer.service --> Generating Token",user.generateJWT());
res.render('oauth-redirect', {
webappRedirectURL: webappRedirectURL,
success: true,
timerCounter: 5,
accessToken: user.generateJWT(),
error: undefined
});
}
})(req, res);
}
catch (error) {
console.log("customerService.js ==> socialTypeLoginCallback -->",error);
}
};
Thanks for help in advance!
I have doubt there somewhere I have returned a callback or response so getting 2nd error but for 1st don't know reasons scope seems to be correct!
In socialTypeLogin
add line
oauthOptions['session'] = false;

Seeding my database for FoodApp API using Faker cant use both import and require in the same file

I am trying us use the Faker npm package to seed data into my new database so that I can properly test my filters. The issue is that I am unable to use both require and import in the same app. All of my other packages make sure of require while faker has to use import. I have come across a few suggested fixes that all don't seem to work.
What I've done so far is to include the below lines of code at he top of my server.js as well as have added type: module to my package.json.
I think the issue may have something to do with how my routes are configured but I'm not 100% sure.
The error I am getting with this configuration is as below
node:internal/errors:465
ErrorCaptureStackTrace(err);
^
Error [ERR_REQUIRE_ESM]: require() of ES Module C:\Users\darre\Desktop\Web Development\foodappbackend\routes\subscribers.js from C:\Users\darre\Desktop\Web Development\foodappbackend\server.js not supported.
subscribers.js is treated as an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which declares all .js files in that package scope as ES modules.
Instead rename subscribers.js to end in .cjs, change the requiring code to use dynamic import() which is available in all CommonJS modules, or change "type": "module" to "type": "commonjs" in C:\Users\darre\Desktop\Web Development\foodappbackend\package.json to treat all .js files as CommonJS (using .mjs for all ES modules instead).
at file:///C:/Users/darre/Desktop/Web%20Development/foodappbackend/server.js:28:27
at async Promise.all (index 0) {
code: 'ERR_REQUIRE_ESM'
}
ADDED CODE AT TOP OF SERVER.JS
import {createRequire} from "module";
const require = createRequire(
import.meta.url
);
PACKAGE.JSON
{
"name": "foodappbackend",
"version": "1.0.0",
"description": "",
"type": "module",
"main": "app.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"#faker-js/faker": "^7.2.0",
"bcrypt": "^5.0.1",
"body-parser": "^1.20.0",
"cors": "^2.8.5",
"dotenv": "^16.0.1",
"express": "^4.18.1",
"express-session": "^1.17.3",
"mongoose": "^6.3.3",
"passport": "^0.5.3",
"passport-facebook": "^3.0.0",
"passport-google-oauth20": "^2.0.0",
"passport-local": "^1.0.0",
"passport-local-mongoose": "^7.1.0"
}
}
SERVER.JS
import {createRequire} from "module";
const require = createRequire(
import.meta.url
);
require("dotenv").config();
const express = require("express");
const mongoose = require("mongoose");
const app = express();
const cors = require("cors")
mongoose.connect(process.env.DATABASE_URL)
const db = mongoose.connection
db.on("error", () => console.error(error))
db.once("open", () => console.log("connected to database"))
app.use(express.json())
app.use(cors())
const subscribersRouter = require("./routes/subscribers")
const restaurantsRouter = require("./routes/restaurants")
const ordersRouter = require("./routes/orders")
app.use("/subscribers", subscribersRouter)
app.use("/restaurants", restaurantsRouter)
app.use("/orders", ordersRouter)
app.listen(3000, () => {
console.log("Server has started on port 3000")
});
RESTUARANTS.js
const express = require("express")
const router = express.Router()
const Restaurant = require("../models/restaurant")
// router.get("/test", (req, res) => {
// const randomName = faker.name.firstName()
// console.log(randomName)
// })
// FILTER OPTIONS
router.get("/filter", async (req, res) => {
//USERS CHOSEN CATEGORIES SENT THROUGH THE REQUEST
const chosenCats = await req.body.categories
var spendPerHead = await req.body.spendperhead
const numberOfHeads = await req.body.numberofheads
if (spendPerHead != null && spendPerHead.length === 1){
const duplicateItems = (arr, numberOfRepetitions) =>
arr.flatMap(i => Array.from({
length: numberOfRepetitions
}).fill(i));
spendPerHead = duplicateItems(spendPerHead, numberOfHeads);
}else{
console.log("no SPH duplication needed")
}
console.log(spendPerHead)
// RETURNS ONLY RESTURANT OPTIONS WITH CATEGORIES CONTAINING AT LEAST ONE OPTION IN THE USERS REQUESTED CATEGORIES
let foundMatch = await Restaurant.aggregate(
[{
$match: {
categories: {
$in: chosenCats
}
}
}]
)
//RESULT OF ALL MENUE ITEMS MATCHING USER CATEGORIES
let result = []
//FULL RESULT OF BOTH RESTURANTS MATCHING USERS CHOSEN CATEGORIES AND MENUE ITEMS OF THOSE RESTURANTS MATCHING USERS CATEGORIES
let fullResult = []
// LOOPS THROUGH ALL RESTURANT OPTIONS MENUES AND OUTPUTS MENUE ITEMS MATCHING THE USERS CHOSEN CATEGORIES
for (let i = 0; i < foundMatch.length; i++) {
foundMatch[i].menue.filter(function checkOptions(option) {
for (let x = 0; x < option.categories.length; x++) {
if (option.categories[x] === chosenCats[0] || option.categories[x] === chosenCats[1] || option.categories[x] === chosenCats[2] || option.categories[x] === chosenCats[3] || option.categories[x] === chosenCats[4] || option.categories[x] === chosenCats[5] || option.categories[x] === chosenCats[6]) {
// FILTERS RESULTS BASED ON TOTAL SPEND PER HEAD CHOSEN BY USER
if (option.price <= spendPerHead[1]) {
result.push(option)
}else if (spendPerHead === undefined){
result.push(option)
}
}
}
})
}
//PUSHES BOTH RESTURANT FILTER RESULT AND MENUE ITEM OPTION FILTER RESULT INTO A SINGLE ARRAY TO BE SENT AS A JSON RESPONSE BY THE SERVER
fullResult.push(result)
fullResult.push(foundMatch)
// console.log(result)
try {
// position 0 == menue option result position 1 == resturant options result
res.json(fullResult)
} catch (err) {
if (err) {
res.status(500).json({
message: err.message
})
}
}
})
// Getting All
router.get("/", async (req, res) => {
try {
const restaurants = await Restaurant.find()
res.json(restaurants)
} catch (err) {
res.status(500).json({
message: err.message
})
}
})
// Getting One
router.get("/:id", getRestaurant, (req, res) => {
res.json(res.restaurant)
})
// Creating One
router.post("/createrestaurant", async (req, res) => {
const restaurant = new Restaurant({
src: req.body.src,
title: req.body.title,
description: req.body.description,
menue: req.body.menue,
rating: req.body.rating,
categories: req.body.categories
})
try {
const newRestaurant = await restaurant.save()
res.status(201).json(newRestaurant)
} catch (err) {
res.status(400).json({
message: err.message
})
}
})
// Updating One
router.patch("/:id", getRestaurant, async (req, res) => {
if (req.body.name != null) {
res.restaurant.name = req.body.name
}
if (req.body.title != null) {
res.restaurant.title = req.body.title
}
if (req.body.description != null) {
res.restaurant.description = req.body.description
}
if (req.body.menue != null) {
const currentMenue = res.restaurant.menue
const newMenueItem = req.body.menue
currentMenue.push(newMenueItem)
}
try {
const updatedRestaurant = await res.restaurant.save()
res.json(updatedRestaurant)
} catch (err) {
res.status(400).json({
message: err.message
})
}
})
// Deleting One
router.delete("/:id", getRestaurant, async (req, res) => {
try {
await res.restaurant.remove()
res.json({
message: "Deleted Restaurant"
})
} catch (err) {
res.status(500).json({
message: err.message
})
}
})
async function getRestaurant(req, res, next) {
let restaurant
try {
restaurant = await Restaurant.findById(req.params.id)
if (restaurant == null) {
return res.status(404).json({
message: "cannot find Restaurant"
})
}
} catch (err) {
return res.status(500).jsong({
message: err.message
})
}
res.restaurant = restaurant
next()
}
module.exports = router
SUBSCRIBERS.JS
const express = require("express");
const router = express.Router();
const Subscriber = require("../models/subscriber");
const passport = require("passport");
const session = require("express-session");
const Order = require("../models/order");
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const facebookStrategy = require('passport-facebook').Strategy;
router.use(session({
secret: "foodsecrets",
resave: false,
saveUninitialized: false
}));
router.use(passport.initialize());
router.use(passport.session());
passport.use(Subscriber.createStrategy());
passport.serializeUser(function (user, done) {
done(null, user.id);
});
passport.deserializeUser(function (id, done) {
Subscriber.findById(id, function (err, user) {
done(err, user);
});
});
// Google auth routes
passport.use(new GoogleStrategy({
clientID: "330178790432-ro0cr35k37f7kq4ln4pdq6dqdpqqtri6.apps.googleusercontent.com",
clientSecret: "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",
callbackURL: "http://localhost:3000/subscribers/google/callback",
},
function (accessToken, refreshToken, profile, email, done) {
//check user table for anyone with a facebook ID of profile.id
const ID = JSON.stringify(email.id)
console.log(ID)
Subscriber.findOne({
googleID: ID
}, function (err, user) {
if (err) {
return done(err);
}
//No user was found... so create a new user with values from Facebook (all the profile. stuff)
if (!user) {
const subscriber = new Subscriber({
googleID: ID,
username: email.displayName,
email: email.emails[0].value,
provider: 'google',
//now in the future searching on User.findOne({'facebook.id': profile.id } will match because of this next line
google: profile._json
});
subscriber.save(function (err) {
if (err) console.log(err);
return done(err, user);
});
} else {
//found user. Return
return done(err, user);
}
});
}));
router.get("/google",
passport.authenticate("google", {
scope: ["profile", "email"]
})
);
router.get("/google/callback",
passport.authenticate("google", {
successRedirect: "https://www.youtube.com/",
failureRedirect: "/login/failed",
})
);
// Facebook Auth Routes
passport.use(new facebookStrategy({
clientID: "1142565916475628",
clientSecret: "f0c297bf99f71d090b317cdcaa5ae6d8",
callbackURL: "http://localhost:3000/subscribers/facebook/callback",
profileFields: ["email", "displayName", "name"]
},
function (accessToken, refreshToken, profile, done) {
//check user table for anyone with a facebook ID of profile.id
console.log(profile)
const ID = JSON.stringify(profile.id)
console.log(ID)
Subscriber.findOne({
facebookID: ID
}, function (err, user) {
if (err) {
return done(err);
}
//No user was found... so create a new user with values from Facebook (all the profile. stuff)
if (!user) {
const subscriber = new Subscriber({
facebookID: ID,
username: profile.displayName,
email: profile._json.email,
provider: profile.provider,
//now in the future searching on User.findOne({'facebook.id': profile.id } will match because of this next line
facebook: profile._json
});
subscriber.save(function (err) {
if (err) console.log(err);
return done(err, user);
});
} else {
//found user. Return
return done(err, user);
}
});
}
));
router.get("/facebook",
passport.authenticate("facebook", {
scope: [ "email"]
})
);
router.get("/facebook/callback",
passport.authenticate("facebook", {
successRedirect: "https://www.youtube.com/",
failureRedirect: "/login/failed",
})
);
// Edit cart (user must be authenticated)
router.patch("/editcart", async (req, res) => {
// DETERMINES IF USER IS AUTH AND IF ADD OR REMOVE ITEM MAKE SURE ADD OR REMOVE PROP IS OUTSIDE OF CART ITEM OBECT
if (req.isAuthenticated() && req.body.addOrRemoveItem === "add") {
var sub
// FINDS SUBSCRIBER BASED ON REQUEST
sub = await Subscriber.findById(req.user._id, function (err, docs) {
if (err) {
console.log(err)
} else {
console.log("founduser1")
}
}).clone()
console.log(sub.cart)
// PUSHES ITEM FROM REQUEST INTO SUBSCRIBERS CART
const currentCart = sub.cart
const newCartItem = req.body.cartItem
await currentCart.push(newCartItem)
// DETERMINES IF USER IS AUTH AND IF ADD OR REMOVE ITEM MAKE SURE REMOVE ITEM PROP IS NOT IN CARTITEM OBJECT
} else if (req.isAuthenticated() && req.body.addOrRemoveItem === "remove") {
var sub
sub = await Subscriber.findById(req.user._id, function (err, docs) {
if (err) {
console.log(err)
} else {
console.log("founduser")
}
}).clone()
// REMOVES A CART ITEM BASED ON ITEM ID MUST PASS IN CART ITEM ID ONLY REMOVES OFF OF SPCIFIC ITEM ID
const cartItemId = req.body.id
await Subscriber.updateOne({
_id: sub._id
}, {
$pull: {
cart: {
_id: cartItemId
}
}
})
} else {
console.log("not reading")
}
try {
// SAVES THE CHANGES IN THE SUBSCRIBERS COLLECTION
const updatedSubscriber = await sub.save()
res.json(updatedSubscriber)
} catch (err) {
console.log(err)
}
})
// Create Order (user must be authenticated)
router.post("/createorder", async (req, res) => {
if (req.isAuthenticated()) {
try {
// FINDS SUBSCRIBER BASED ON REQUEST ID
const sub = await Subscriber.findById(req.user._id, function (err, docs) {
if (err) {
console.log(err)
} else {
console.log("founduser")
}
}).clone()
//STORES/TARGETS THE PENDING ORDER OF SUBSCRIBER
const pendingOrder = await sub.pendingOrder
//DETERMINES IF THE USER ALREADY HAS A PENDING ORDER, IF USER HAS PENDING ORDERS THEY WILL BE BLOCKED FROM CREATING A NEW ORDER UNTIL THE PREVIOUS ORDER IS CONFIRMED OR CANCELLED
// IDENTIFIES SPECIFIC CART BASED ON REQUEST
const cart = req.user.cart
// STORES/TARGETS THE CART OF THE SUBSCRIBER
const subCart = sub.cart
//MAPS THE PRICE OF EACH CART ITEM TO AN ARRAY
const itemTotals = cart.map(prop => prop.price)
//FUNCTION FOR SUMMING ALL VALUES IN AN ARRAY
const reducer = (accumulator, curr) => accumulator + curr;
//USES REDUCER FUNCTION TO SUM ALL PRICES OF ITEMS IN CART
const total = itemTotals.reduce(reducer)
//CREATES A NEW ORDER USING THE ABOVE STORED VARIABLES
const order = new Order({
userID: req.user._id,
total: total,
items: cart,
confirmed: false
})
// PUSHES NEW PENDING ORER INTO SUBSCRIBERS PENDING ORDER ARRAY
await pendingOrder.push(order)
//EMPTIES THE SUBSCRIBERS CART
await subCart.splice(0, subCart.length);
// SAVES THE NEW ORDER TO THE MAIN ORDERS COLLECTION & THE SUBS PENDING ORDER
const newOrder = await order.save()
const newPendingOrder = await sub.save()
//SENDS BACK BOTH THE ORDERS COLLECTION AND USERS ORDER HISTORY ARRAY
res.status(201).send({
newOrder,
newPendingOrder
})
} catch (err) {
res.status(400).json({
message: err.message
})
}
}
})
// GET ONE SUBSCRIBER BASED ON REQUEST ID USING PASSPORT IDEALLY USED FOR DATA NEEDED FOR THE PAYMENT PAGE AFTER MAKING AN ORDER
router.get("/getone", async (req, res) =>{
if (req.isAuthenticated()){
const sub = await Subscriber.findById(req.user._id, function (err, docs) {
if (err) {
console.log(err)
} else {
console.log("founduser")
}
}).clone()
try {
res.json(sub)
} catch (err) {
res.status(500).json({
message: err.message
})
}
}
})
// CONFIRMS ORDER ON POST REQUEST RESULTING FROM A PAYMENT CONFIRMATION ON THE FRONTEND
router.post("/confirmorder", async (req, res) => {
if (req.isAuthenticated) {
const sub = await Subscriber.findById(req.user._id, function (err, docs) {
if (err) {
console.log(err)
} else {
console.log("founduser")
}
}).clone()
const pendingOrder = await sub.pendingOrder
const subOrderHistory = await sub.orderHistory
const mainOrder = await Order.findById(pendingOrder[0]._id, function (err, docs) {
if (err) {
console.log(err)
} else {
console.log("Found Order")
}
}).clone()
console.log(mainOrder)
await subOrderHistory.push(pendingOrder[0]);
mainOrder.confirmed = true
try {
pendingOrder.splice(0, pendingOrder.length);
const updatedOrder = await mainOrder.save()
const updatedSub = await sub.save()
res.status(201).send({
updatedOrder,
updatedSub
})
} catch (err) {
res.status(400).json({
message: err.message
})
}
}
})
// GETS ALL SUBSCRIBERS
router.get("/getall", async (req, res) => {
if (req.isAuthenticated()) {
try {
const subscribers = await Subscriber.find()
res.json(subscribers)
} catch (err) {
res.status(500).json({
message: err.message
})
}
}
});
// DELIVERS ALL DATA NEEDED FOR LOGGED IN HOMEPAGE BASED ON IF THE USER IS AUTHENTICATED
router.get("/loggedin", async (req, res) => {
if (req.isAuthenticated()) {
try {
const subscribers = await Subscriber.findById(req.user._id, function (err, docs) {
if (err) {
console.log(err)
} else {
console.log("Found User!")
}
}).clone()
res.json(subscribers)
} catch (err) {
res.status(500).json({
message: err.message
})
}
}
});
// // Getting One
// router.get("/:id", getSubscriber, (req, res) => {
// res.json(res.subscriber)
// });
// LOGIN USING PASSPORT JS
router.post("/login", (req, res) => {
const subscriber = new Subscriber({
username: req.body.username,
password: req.body.password,
email: req.body.email
});
req.login(subscriber, async function (err) {
if (err) {
console.log(err)
} else {
try {
passport.authenticate("local")(req, res, function () {
console.log("Authenticated")
console.log(req)
res.status(201).json("authenticated")
})
} catch (err) {
res.status(400).json({
message: err.message
})
}
}
})
})
// REGISTER USING PASSPORT JS
router.post("/register", async (req, res) => {
Subscriber.register({
username: req.body.username,
email: req.body.email
}, req.body.password, async (err, subscriber) => {
if (err) {
console.log(err)
} else {
try {
await passport.authenticate("local")(req, res, function () {
console.log("is authenticated")
res.status(201).json(newSubscriber)
})
const newSubscriber = await subscriber.save()
} catch (err) {
res.status(400).json({
message: err.message
})
}
}
});
})
// UPDATES ONE SUBSCRIBER BASED ON THE SUBSCRIBERS ID
router.patch("/:id", getSubscriber, async (req, res) => {
if (req.body.email != null) {
res.subscriber.email = req.body.email
}
if (req.body.password != null) {
res.subscriber.password = req.body.password
}
try {
const updatedSubscriber = await res.subscriber.save()
res.json(updatedSubscriber)
} catch (err) {
res.status(400).json({
message: err.message
})
}
})
// DELETES ONE SUBSCRIBER BASED ON THE SUBSCRIBERS ID
router.delete("/:id", getSubscriber, async (req, res) => {
try {
await res.subscriber.remove()
res.json({
message: "Deleted Subscriber"
})
} catch (err) {
res.status(500).json({
message: err.message
})
}
})
// FUNCTION FOR GETTING A SPECIFIC SUBSCRIBER FROM THE SUBSCRIBERS COLLECTION BASED ON A PRIOVIDED ID IN THE REQUEST PARAMATERS
async function getSubscriber(req, res, next) {
let subscriber
try {
subscriber = await Subscriber.findById(req.params.id)
if (subscriber == null) {
return res.status(404).json({
message: "cannot find subscriber"
})
}
} catch (err) {
return res.status(500).json({
message: err.message
})
}
res.subscriber = subscriber
next()
}
module.exports = router
You can use faker with require:
// node v14.18.1
const {faker} = require('#faker-js/faker');
console.log(faker.datatype.uuid());
If you check the package you will see that inside dist folder it has both esm and cjs versions.

Node.js:"Cannot read property 'toString' of undefined

I'm following a restApi course with Node.js, It's a blog API. my problem is when deleting a post for the unAuthorized user it first gives me a 500 error
"error": "Cannot read property 'toString' of undefined"
. but when doing it again it gives me
Post not found with id of.
Of course, it supposed to give me
not authorized to delete this post.
Update the post is also the same, I even tried to copy/paste the code from the course but the same problem.
postController
exports.deletePost = asyncHandler(async (req, res, next) => {
const post = await Post.findByIdAndDelete(req.params.id);
if (!post) {
return next(
new ErrorResponse(`Post not found with id of ${req.params.id}`, 404)
);
}
// Make sure user is post owner
if (post.user.toString() !== req.user.id) {
return next(
new ErrorResponse(
`User ${req.params.id} is not authorized to delete this post`,
401
)
);
}
post.remove();
res.status(200).json({ success: true, data: post});
});
updatePost
exports.updatePost = asyncHandler(async (req, res, next) => {
let post = await Post.findById(req.params.id);
if (!post) {
return next(
new ErrorResponse(`Post not found with id of ${req.params.id}`, 404)
);
}
// Make sure user is post owner
if (post.user.toString() !== req.user.id) {
return next(
new ErrorResponse(
`User ${req.params.id} is not authorized to update this post`,
401
)
);
}
post = await Post.findOneAndUpdate(req.params.id, req.body, {
new: true,
runValidators: true
});
res.status(200).json({ success: true, data: post });
});
You could introduce a type check on the post.user object to ensure that the user exists within the post.
if (typeof post.user == "undefined" || post.user.toString() !== req.user.id)
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof
I have tried the code
if (typeof post.user == "undefined" || post.user.toString() !== req.user.id)
But after using this cause to get me an error 'User not Authorized' in my error handling.
In my case, I have to convert the req.user.id to an integer
if (post.user !== req.user.id.parseInt)
const express = require("express");
const router = express.Router();
const fetchuser = require("../middleware/Fetchuser");
const Notes = require("../models/Notes.js");
const { body, validationResult } = require("express-validator");
router.get("/fetchnotes", fetchuser, async (req, res) => {
try {
const notes = await Notes.find({ user: req.user });
res.json(notes);
} catch (error) {
console.log(error.message);
res.status(500).send("error occured");
}
});
router.post(
"/addnotes",
[
body("title").isLength({ min: 5 }),
body("description").isLength({ min: 3 }),
],
fetchuser,
async (req, res) => {
try {
const { title, description, tag } = req.body;
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const note = new Notes({
title,
description,
tag,
user: req.user.id,
});
const savedNotes = await note.save();
res.json(savedNotes);
} catch (error) {
console.log(error.message);
res.status(500).send("error occured");
}
}
);
router.put("/updatenotes/:id", fetchuser, async (req, res) => {
const { title, description, tag } = req.body;
const newNote = {};
if (title) {
newNote.title = title;
}
if (description) {
newNote.description = description;
}
if (tag) {
newNote.tag = tag;
}
let note = await Notes.findById(req.params.id);
if (!note) {
return res.status(400).send("error occured");
}
if (note.user !== req.user.id.parseInt) {
return res.status(401).json("user not allowed");
}
note = await Notes.findByIdAndUpdate(
req.params.id,
{ $set: newNote },
{ new: true }
);
res.json(note);
});
module.exports = router;

Mongoose: How to post into array inside Schema

I have a Company Schema that will hold some data for that company and an array of posts. When a user submits a post I use passport to decode the token and get some user information. Inside that user information there is an object ID which allows me to find the company that the user belongs to.
So now I have found the company that the user belongs to I need to save the submitted post into the board_posts array inside this company
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const BoardPostSchema = new Schema({
name: {
type: String
}
});
const CompanySchema = new Schema({
company_name: {
type: String
},
board_posts: [BoardPostSchema],
});
module.exports = Company = mongoose.model('companies', CompanySchema);
router.post('/new-opportunity', passport.authenticate('jwt', {
session: false
}), (req, res) => {
let user = req.user;
let newPost = req.body;
let companyId = user.company_id;
const boardPost = {
name: newPost.name
};
Company.find({'_id': companyId})
.then(company => {
// push boardPost into this company.board_posts array
})
.catch(error => {
});
});
An alternative solution with findByIdAndUpdate:
router.post("/new-opportunity", passport.authenticate("jwt", { session: false }), (req, res) => {
let user = req.user;
let newPost = req.body;
let companyId = user.company_id;
const boardPost = {
name: newPost.name,
};
Company.findByIdAndUpdate(
companyId,
{
$push: {
board_posts: boardPost,
},
},
{
new: true,
}
)
.then((company) => {
console.log("Updated compay if found:", company);
res.send(company);
})
.catch((error) => {
console.log(error);
res.status(500);
});
});
Or if you want only update status, you can use updateOne:
router.post("/new-opportunity", passport.authenticate("jwt", { session: false }), (req, res) => {
let user = req.user;
let newPost = req.body;
let companyId = user.company_id;
const boardPost = {
name: newPost.name,
};
Company.updateOne(
{ _id: companyId },
{
$push: {
board_posts: boardPost,
},
}
)
.then((result) => {
console.log(result);
// result.n; // Number of documents matched
// result.nModified; // Number of documents modified
res.send(result);
})
.catch((error) => {
console.log(error);
res.status(500);
});
});
You can use $push and update
router.post('/new-opportunity', passport.authenticate('jwt', {
session: false
}), (req, res) => {
let user = req.user;
let newPost = req.body;
let companyId = user.company_id;
const boardPost = {
name: newPost.name
};
Company.update({_id: user.company_id},{
$push{
//the things you want to add
}
});
});
Hopefully this is what you want to do!
Yes you can use the $pushand findOneAndUpdate operator. It would look nicer if you use the async/await approach.
router.post('/new-opportunity', passport.authenticate('jwt', {
session: false
}), async (req, res) => {
let user = req.user;
let newPost = req.body;
let companyId = user.company_id;
const boardPost = {
name: newPost.name
};
let response = await Company.findOneAndUpdate({'_id': companyId}, {
$push: {
board_posts: "testword1"
}
},{ new: true }) //i set this to true so mongodb will return me the new updated document
res.send(response);
});

Soft delete using nodejs + mongodb

How do i perform a soft delete using nodejs on mongodb
for example using this code, can it be modified to do a soft delete instead or is there another way?
Controllers/ category.js
exports.remove = (req, res) => {
const category = req.category;
category.remove((error, data) => {
if (error) {
return res.status(400).json({
error: errorHandler(error)
});
}
res.json({
message: "Category deleted"
});
});
};
routes/category.js
const express = require("express");
const router = express.Router();
const { create, categoryById, read, update, remove, list } = require("../controllers/category");
const { requireSignin, isAuth, isAdmin } = require("../controllers/auth");
const { userById } = require("../controllers/user");
router.get("/category/:categoryId", read);
router.post("/category/create/:userId", requireSignin, isAuth, isAdmin, create);
router.put("/category/:categoryId/:userId", requireSignin, isAuth, isAdmin, update);
router.delete("/category/:categoryId/:userId", requireSignin, isAuth, isAdmin, remove);
router.post("/categories", list);
router.param("categoryId", categoryById);
router.param("userId", userById);
module.exports = router;
models/category.js
const mongoose = require("mongoose");
const categorySchema = new mongoose.Schema(
{
name: {
type: String,
trim: true,
required: true,
maxlength: 32
}
},
{ timestamps: true }
);
module.exports = mongoose.model("Category", categorySchema);
I'm not sure req.category is instance of model or model itself.
So in my answer below I assume that somehow You've got instance of model and injected it as req.category
1) Add deleted field to schemas where You want to have soft delete:
const mongoose = require("mongoose");
const {Schema} = mongoose;
const categorySchema = new Schema(
{
name: {
type: Schema.Types.String,
trim: true,
required: true,
maxlength: 32
},
// deleted flag for soft delete feature
deleted: {
type: Schema.Types.Boolean,
index: true,
default: false
}
},
{ timestamps: true }
);
module.exports = mongoose.model("Category", categorySchema);
2) Change delete procedure:
module.exports.remove = async (req, res) => {
try {
const category = req.category; // if it's an instance of model
// or if it's mongoose model comment line above
// const category = await req.category.findOne({
// _id: req.params.categoryId,
// deleted: false
// });
if (!category || category.deleted === true) {
return res.status(404).json({
error: 'Requested category does not exist'
});
}
category.deleted = true;
await category.save();
res.status(200).json({
message: "Category deleted"
});
}
catch (error) {
res.status(400).json({
error: errorHandler(error)
});
}
};
3) Change category read route: /category/:categoryId handler:
module.exports.read = async (req, res) => {
try {
const category = req.category; // if it's an instance of model
// or if it's mongoose model comment line above
// const category = await req.category.findOne({
// _id: req.params.categoryId,
// deleted: false
// });
if (!category || category.deleted === true) {
return res.status(404).json({
error: 'Requested category does not exist'
});
}
res.status(200).json(category);
}
catch (error) {
res.status(400).json({
error: errorHandler(error)
});
}
};
4) Change listing procedure:
module.exports.list = async (req, res) => {
try {
const categories = await req.category.find({deleted: false});
res.status(200).json({categories});
}
catch (error) {
res.status(400).json({
error: errorHandler(error)
});
}
};
Step 1:
Add a new field in your Schema "Deleted" with type Boolean and Default value 'false'
deleted: { type: Boolean, default: false }
Step 2
Change Delete Process To:
router.delete('/:id', verifyTokenAndAuthorization, async (req, res) => {
try {
await User.findByIdAndUpdate(req.params.id, { deleted: true }); <= change delete status to 'true'
res.status(200).json('user Deleted');
} catch (error) {
res.status(500).json(error)
}
})

Categories