findByIdAndUpdate do not update document - mongodb

I am trying to update a field to the document with findByIdAndUpdate. The field I am trying to update is defined in the Bar Model. And I can also assure that req.body.bookId has a valid id.
Here's how my request looks,
app.patch("/foo", async (req, res) => {
try {
await validateId(req.body.bookId);
let doc = await Bar.findByIdAndUpdate(
req.body.bookId,
{ DateT: Date.now() },
{ new: true }
);
res.send(doc);
} catch (err) {
console.log(err);
}
});
Bar schema,
const mongoose = require("mongoose");
const barSchema = mongoose.Schema({
bookId: {
type: String,
unique: true,
},
DateT: {
type: Date,
default: null,
},
});
module.exports = mongoose.model("Bar", barSchema);

use updateOne, when you use async don't use .then() use try/catch
test it:
app.patch("/foo", async (req, res) => {
try {
let doc = await Bar.updateOne(
{ bookId : req.body.bookId },
{ DateT: Date.now() },
{ new: true }
);
res.send(doc);
} catch (error) {
console.log(error);
}
});

app.patch("/foo", async (req, res) => {
await Bar.findByIdAndUpdate(
req.body.bookId,
{ DateT: Date.now()},
(err, docs) => {
if (err) {
console.log(err);
} else {
res.send(docs);
}
}
);
});

Related

Mongoose pre save middleware is not triggering/saving hashed passwords to db

I am trying to pre save and hash password with bcrypt in mongoose in my next.js project, but password still unhashed. i tryed every link in stackoverflow and didnt solve it, the password still saved unHashed.
mongoose version: 6.9.1
this is my users.model file:
import {
models,
model,
Schema,
} from 'mongoose';
import bcrypt from 'bcrypt';
const UserSchema: Schema = new Schema({
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
displayName: {
type: String,
required: true,
},
role: {
type: String,
},
});
UserSchema.pre('save', function (next) {
console.log('Pre-Save Hash has fired.');
let user = this;
bcrypt.genSalt(10, (err, salt) => {
if (err) console.error(err);
bcrypt.hash(user.password, salt, (err, hash) => {
user.password = hash;
next();
});
});
});
const UserModel = models.Users || model('Users', UserSchema, 'users');
export default UserModel;
this is my adding function file:
import dbConnect from '#/utils/mongodb';
import UserModel from '#/models/user.model';
import { NextApiRequest, NextApiResponse } from 'next';
import { MongoError } from 'mongodb';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
// const { email, password } = req.query;
try {
dbConnect();
const query = req.body;
const newUser = new UserModel(query);
const addedUser= await newUser.save(function (err: MongoError) {
if (err) {
throw err;
}
});
res.status(200).json(addedUser);
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Internal server error' });
}
}
i cant see the 'Pre-Save Hash has fired.' in my console also..
// You need to add user.isModified("password")
userSchema.pre("save", function (next) {
var user = this;
if (user.isModified("password")) {
bcrypt.genSalt(SALT_I, (err, salt) => {
if (err) {
return next(err);
}
bcrypt.hash(user.password, salt, (err, hash) => {
if (err) {
return next(err);
}
user.password = hash;
next();
});
});
} else {
next();
}
});
userSchema.methods.comparePassword = function (candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, (err, isMatch) => {
if (err) return cb(err);
cb(null, isMatch);
});
};
// to make register end point
import mongoose from "mongoose";
import User from "../../../models/User";
const dbConnect = async () => {
mongoose
.connect("mongodb://localhost:27017/test")
.then(() => {
console.log("Connected to mongoDb");
})
.catch((error) => {
console.log(error);
});
};
export default async function handler(req, res) {
try {
await dbConnect();
const query = req.body;
const newUser = new User(query);
await newUser.save(function (err, result) {
if (err) {
throw err;
} else {
res.status(200).json(result);
}
});
} catch (error) {
console.error(error);
res.status(500).json({ message: "Internal server error" });
}
}
thanks to all.
The problem was in my dbconnect file!

Is there an easier way to post an element in the html-post method?

const express = require("express");
const cors = require("cors");
const dotenv = require("dotenv");
const bodyParser = require("body-parser");
const mongoose = require("mongoose");
const app = express();
dotenv.config();
app.use(cors());
app.use(bodyParser.json());
const { Schema } = mongoose;
const userSchema = Schema({
imageUrl: { type: String },
description: { type: String },
title: { type: String },
price: { type: Number },
});
const Users = mongoose.model("users", userSchema);
app.get("/", (req, res) => {
res.send("started");
});
`get metod`
app.get("/users", (req, res) => {
Users.find({}, (err, docs) => {
if (!err) {
res.send(docs);
} else {
res.status(404).json({ message: err });
}
});
});
app.get("/users/:id", (req, res) => {
const { id } = req.params;
Users.findById(id, (err, doc) => {
if (!err) {
if (doc) {
res.send(doc);
}
} else {
res.status(404).json({ message: err });
}
});
});
`delete metod`
app.delete("/users/:id", (req, res) => {
const { id } = req.params;
Users.findByIdAndDelete(id, (err, doc) => {
if (!err) {
res.send("Succesfully deleted");
} else {
res.status(404).json({ message: err });
}
});
});
`post metod`
app.post("/users", (req, res) => {
const obj = {
imageUrl: req.body.imageUrl,
description: req.body.description,
title: req.body.title,
price: req.body.price,
};
console.log(obj);
let user = new Users(obj);
user.save();
res.send({ message: " Succesfully added" });
});
const PORT = process.env.PORT;
const url = process.env.URL.replace("<password>", process.env.PASSWORD);
mongoose.set("strictQuery", true);
mongoose.connect(url, (err) => {
if (!err) {
console.log("DB connected");
app.listen(PORT, () => {
console.log("Server start");
});
}
});
I'm trying to learn how exactly get post delete queries work
I'm trying to reduce the code here, but no matter what I do, small errors appear in the end. I have a json string, I want to pass it to POST method. But the 'execute', and 'executeMethod ' are throwing error as below:
"The method execute(HttpUriRequest) in the type HttpClient is not applicable for the arguments (PostMethod)". i have included the depencencies.

Updating array of objects in Mongoose

I can't handle updating array of objects in my database, tried many options but nothing worked. Im pretty sure that the answer is obvious, but I couldn't manage it since wednesday.
Here is my kitSchema:
const kitSchema = new mongoose.Schema({
email: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
kit: {
type: Array,
required: true,
},
profiles: {
type: Array,
required: true,
},
});
module.exports = mongoose.model("Kit", kitSchema);
All users have their own document, and there are also profiles in it. I want to update single profile by passing the id of user and id of profile.
Example of data:
_id: 1,
email: "abc#mail",
password: "abc",
profiles: [
{
id: 1,
name: John
},
]
And here's my latest solution which doesn't work:
router.put("/profile/:id", async (req, res) => {
let kit = await Kit.findById(req.params.id, (error, data) => {
if (error) {
console.log(error);
} else {
console.log(data);
}
});
try {
await kit.profiles.findOneAndUpdate(
{ id: req.body.id },
{ name: req.body.name },
{ new: true },
(error, data) => {
if (error) {
console.log(error);
} else {
console.log(data);
}
}
);
try {
res.status(202).json({ message: "Changed" });
} catch (err) {
res.status(400).json({ message: err });
}
} catch (err) {
res.status(400).json({ message: err });
}
});
Could you give me a hand with this?
As always, after days of trying I've got answer 10 minutes after asking question. Here's what I came up with:
router.put("/profile/:id", async (req, res) => {
await Kit.findOneAndUpdate(
{ _id: req.params.id, profiles: { $elemMatch: { id: req.body.id } } },
{
$set: {
"profiles.$.name": req.body.name,
"profiles.$.profilePicture": req.body.profilePicture,
},
},
{ new: true, safe: true, upsert: true },
(error, data) => {
if (error) {
console.log(error);
} else {
console.log(data);
}
}
);
try {
res.status(202).json({ message: "Changed" });
} catch (err) {
res.status(400).json({ message: err });
}
});

How do I use populate with callbacks?

User model
const Schema = mongoose.Schema
const userSchema = new Schema({
username: { type: String, required: true },
email: { type: String, reuired: true },
password: { type: String, required: true },
posts:[{ type: Schema.Types.ObjectId, ref: "Posts" }]
}, { timestamps: true })
Post model
const Schema = mongoose.Schema;
const postSchema = new Schema({
title: { type: String, required: true },
content: { type: String, required: true },
user: { type: Schema.Types.ObjectId, ref: "User" },
}, { timestamps: true }
endpoint for getting all posts with all users information
listPostsWithUsers: (req, res, next) => {
Post.find({}, (error, posts) => {
if (error) {
return res.status(500).json({ error: "something went wrong" })
} else if (!posts) {
return res.status(400).json({ msg: "sorry no posts" })
} else if (posts) {
return res.status(200).json({ posts })
}
})
}
The output should be the returned posts with the user object so that I can identify which post the user has posted.
Now, my question is how do I apply populate() method in the above endpoint. Mostly all examples are with exec() function but I've seen no example with callbacks. It's kind of a syntax problem.
Thank you.
Update#1: The result I'm getting currently.
{
"posts": [
{
"_id": "5e65cce5ebddec0c5cc925ab",
"title": "Neil's Post",
"content": "post by neil.",
"createdAt": "2020-03-09T04:58:13.900Z",
"updatedAt": "2020-03-09T04:58:13.900Z",
"__v": 0
},
{
"_id": "5e65cd32ebddec0c5cc925ad",
"title": "Slash's post",
"content": "post by slash.",
"createdAt": "2020-03-09T04:59:30.180Z",
"updatedAt": "2020-03-09T04:59:30.180Z",
"__v": 0
},
{
"_id": "5e65f430a989612916636e8d",
"title": "Jimmy's post",
"content": "post by jimmy",
"createdAt": "2020-03-09T07:45:52.664Z",
"updatedAt": "2020-03-09T07:45:52.664Z",
"__v": 0
}
]
}
Update#2
users collection.
{
"users": [
{
"posts": [],
"_id": "5e65ccbeebddec0c5cc925aa",
"username": "Neil",
"email": "neily888#gmail.com",
"password": "$2b$10$AHHRKuCX3nakMs8hdVj0DuwD5uL0/TJwkJyKZYR/TXPTrIo9f80IW",
"createdAt": "2020-03-09T04:57:35.008Z",
"updatedAt": "2020-03-09T04:57:35.008Z",
"__v": 0
},
{
"posts": [],
"_id": "5e65cd0eebddec0c5cc925ac",
"username": "Slash",
"email": "slash938#gmail.com",
"password": "$2b$10$QQX/CFJjmpGdBAEogQ4XO.1e1ZowuPCX7pJcHTUav7NfatGgp6sa6",
"createdAt": "2020-03-09T04:58:54.520Z",
"updatedAt": "2020-03-09T04:58:54.520Z",
"__v": 0
},
{
"posts": [],
"_id": "5e65f408a989612916636e8c",
"username": "Jimmy",
"email": "jimmy787#gmail.com",
"password": "$2b$10$/DjwWYIlNswgmYt3vo7hJeNupfBdFGe7p77uisYUViKv8IdhasDC.",
"createdAt": "2020-03-09T07:45:12.293Z",
"updatedAt": "2020-03-09T07:45:12.293Z",
"__v": 0
}
]
}
Update#3:
usersController
const User = require("../models/User")
module.exports = {
createUser: (req, res) => {
User.create(req.body, (err, createdUser) => {
if (err) console.log(err)
res.json({createdUser})
})
},
listUsers: (res) => {
User.find({}, (err, users) => {
if (err) console.log(err)
res.json({users})
})
},
getUser: (req, res) => {
User.findById(req.params.id, (err, user) => {
if (err) console.log(err)
return res.json({user})
})
},
updateUser: (req, res) => {
const user = {
username: req.body.username,
email: req.body.email,
password: req.body.password
}
User.findOneAndUpdate(req.params.id, user, { new: true }, (err, updatedUser) => {
if (err) console.log(err)
res.json({updatedUser})
})
},
deleteUser: (req, res) => {
User.findByIdAndDelete(req.params.id, (err, deleteduser) => {
if (err) console.log(err)
return res.status(200).json({ user: deleteduser })
})
}
}
postsController
const Post = require("../models/Post")
module.exports = {
createPost: (req, res) => {
const data = {
title: req.body.title,
description: req.body.description,
}
Post.create(data, (err, newPost) => {
if (err) console.log(err);
return res.status(200).json({ newPost })
})
},
listPosts: (res) => {
Post.find({}, async (err, posts) => {
if (err) console.log(err);
posts = await Post.populate(posts, {
path: "user",
model: "User"
})
return res.status(200).json({ posts })
})
},
findPost: (req, res) => {
Post.findById(req.params.id, (err, post) => {
if (err) console.log(err);
return res.json({ post })
}
)
},
updatePost: (req, res) => {
const post = {
title: req.body.title,
description: req.body.description
}
Post.findByIdAndUpdate(req.params.id, post, { new: true },(err, updatedPost) => {
if (err) console.log(err);
return res.status(200).json({ updatedPost })
})
},
deletePost: (req, res) => {
Post.findByIdAndDelete(req.params.id, (err, deletedPost) => {
if (err) console.log(err);
return res.status(200).json({ deletedPost })
})
}
}
Update#4
router.get("/posts/:id", usersController.getUserPosts) `
getUserPosts: (req, res) => {
User.findById(req.params.id, async (err, user) => {
if (err) {
return res.status(500).json({ error: "Server error" })
} else if (!user) {
return res.status(400).json({ error: "No user" })
} else if (user) {
user = await User.populate("user", {
path: "posts",
model: "Post"
})
return res.status(200).json({ user })
}
})
}
I suggest you to use mongoose populate.
listPostsWithUsers : (req, res, next) => {
Post.find({}).populate('user').exec(function (err, data) {
if (err) {
console.log(err);
} else {
console.log(data);
}
})
}
You can refer this official mongoose populate document
You can even populate as many fields required by using populate as
populate([{ path: 'user' }, { path: 'book', select: { author: 1 } }])
with the help of select in populate, you can project the required fields (get only those fields from populated collection.)
Edit 1
createPost: (req, res) => {
const data = {
title: req.body.title,
description: req.body.description,
user: req.user._id,
}
Post.create(data, (err, newPost) => {
if (err) console.log(err);
return res.status(200).json({ newPost })
})
You will need to add user field in post while creating.
take user id from token or from query and you would be good to go.
Edit 2
getUserPosts: (req, res) => {
User.findById(req.params.id).populate([{ path: 'posts' }])
.exec(function (err, data) {
if (err) {
console.log(err);
} else {
console.log(data);
}
});
}
You are finding data in user module and you want to populate post data, so you just need to say which field you want to populate as you have already defined on field which model to refer by adding ref with that field in mongoose schema.
getUserPosts: (req, res) => {
User.findById(req.params.id, async (err, user) => {
if (err) {
return res.status(500).json({ error: "Server error" })
} else if (!user) {
return res.status(400).json({ error: "No user" })
} else if (user) {
user = await User.populate("posts")
return res.status(200).json({ user })
}
})
}
This should also work for you.
You can do that after getting the posts as:
listPostsWithUsers: (req, res, next) => {
Post.find({}, async (error, posts) => {
if (error) {
return res.status(500).json({ error: "something went wrong" })
} else if (!posts) {
return res.status(400).json({ msg: "sorry no posts" })
} else if (posts) {
posts = await Post.populate(posts, {
path: 'user',
model: 'User'
});
return res.status(200).json({ posts })
}
})
}

Mongoose update only the values that have changed

I have a PUT route to update value. I am hitting this route from two places. One is sending information about details and one about completed. The problem is that mongoose is updating booth even though it gets value from only one.
So if I send information about completed that it is true and latter I hit this route with new details (that dont have completed value) it will update completed also to false. How do I update just the value that was changed?
router.put('/:id', (req, res) => {
Todo.findOne({_id:req.body.id}, (err, foundObject) => {
foundObject.details = req.body.details
foundObject.completed = req.body.completed
foundObject.save((e, updatedTodo) => {
if(err) {
res.status(400).send(e)
} else {
res.send(updatedTodo)
}
})
})
})
EDIT:
Thanks to Jackson hint I was managed to do it like this.
router.put('/:id', (req, res) => {
Todo.findOne({_id:req.body.id}, (err, foundObject) => {
if(req.body.details !== undefined) {
foundObject.details = req.body.details
}
if(req.body.completed !== undefined) {
foundObject.completed = req.body.completed
}
foundObject.save((e, updatedTodo) => {
if(err) {
res.status(400).send(e)
} else {
res.send(updatedTodo)
}
})
})
})
const updateQuery = {};
if (req.body.details) {
updateQuery.details = req.body.details
}
if (req.body.completed) {
updateQuery.completed = req.body.completed
}
//or
Todo.findOneAndUpdate({id: req.body.id}, updateQuery, {new: true}, (err, res) => {
if (err) {
} else {
}
})
//or
Todo.findOneAndUpdate({id: req.body.id}, {$set: updateQuery}, {new: true}, (err, res) => {
if (err) {
} else {
}
})
Had a function similar to this my approach was this
const _ = require('lodash');
router.put('/update/:id',(req,res, next)=>{
todo.findById({
_id: req.params.id
}).then(user => {
const obj = {
new: true
}
user = _.extend(user, obj);
user.save((error, result) => {
if (error) {
console.log("Status not Changed")
} else {
res.redirect('/')
}
})
}).catch(error => {
res.status(500);
})
};
Taking new : true as the value you updating
It gets kinda ugly as the fields to be updated get increased. Say 100 fields.
I would suggest using the following approach:
try {
const schemaProperties = Object.keys(Todo.schema.paths)
const requestKeys = Object.keys(req.body)
const requestValues = Object.values(req.body)
const updateQuery = {}
// constructing dynamic query
for (let i = 0; i < requestKeys.length; i++) {
// Only update valid fields according to Todo Schema
if ( schemaProperties.includes(requestKeys[i]) ){
updateQuery[requestKeys[i]] = requestValues[i]
}
}
const updatedObject = await TOdo.updateOne(
{ _id:req.params.idd},
{ $set: updateQuery }
);
res.json(updatedObject)
} catch (error) {
res.status(400).send({ message: error });
}