How can I implement transaction concept in mongoose model? - node.js

I have three models "userLogin.js","userDetail.js",and "userAddress.js".I want data should be stored simultaneously, if any error occurs it should rolback all the insert actions.this what I have tried. I gives me the error user is not defined . when try to fix them it gives the error "schema is not registered"
const UserLogin=require("../models/userLogin");
const UserDeatil=require("../models/userDetail");
var myModelSchema1 = require('mongoose').model('UserLogin').schema;
var myModelSchema2 = require('mongoose').model('UserDeatils').schema;
exports.user_signup = (req, res, next) => {
UserLogin.find({ email: req.body.email })
.exec()
.then(user => {
if (user.length >= 1) {
return res.status(409).json({
message: "Mail exists"
});
} else {
bcrypt.hash(req.body.password, 10, (err, hash) => {
if (err) {
return res.status(500).json({
error: err
});
} else {
const user = new UserLogin({
_id: new mongoose.Types.ObjectId(),
email: req.body.email,
password: hash,
loginDate:req.body.logindate,
});
const userdetils = new UserDeatil({
_id: new mongoose.Types.ObjectId(),
userId:result.userID,
userName:req.body.username,
dob:req.body.dob,
gender:req.body.gender,
photo: req.file? req.file.path : null,
imei:req.body.imei,
});
insertUsers();
}
});
}
});
};
async function insertUsers(){
try{
const id= transaction.insert(myModelSchema1, user);
const id1= transaction.insert(myModelSchema2, userdetils);
const final = await transaction.run();
}
catch(error){
console.error(error);
const rollbackObj = await transaction.rollback().catch(console.error);
transaction.clean();
c
}
}

first when you define your users schema the email must be uniqe wich when fails when you tries to create anothe user document with the same email,
and with this convention you can move forward like this:
const UserLogin=require("../models/userLogin");
const UserDeatil=require("../models/userDetail");
cosnt signup = async (req ,res)=>{
const { email , password ,...details} = req.body
const createdDocs = []
const hashedPwd = hash(password);
try{
const user = new UserLogin({ email , password: hashedPwd });
await user.save()
createdDocs.push(user)
const userDetails = new UserDetails({...details,userId:user._id});
await userDetails.save()
createdDocs.push(userDetails)
catch(err){
res.json({ status:false, message:err.message})
//emulates the rollback when any thing fails on the try flow
if(createdDocs.length){
const operationsToRollBack = createdDocs.map(doc=>doc.remove)
await Promise.all(operationsToRollBack)
}
}

MongoDB supports multi-document transactions starting from version 4.0.
Ideally, if you need a transactional database you would use an SQL type db.
But if you would still like to enjoy MongoDB while needing transactions, they have introduced an API for this - https://docs.mongodb.com/manual/core/transactions/

Related

Email response from db to a user in node.js

in my appcliation I generate a new user password (PUT request). This has to be sent to the updated users emailadress which is in the database. I am pretty new to node and express and can use some help figuring out how to do this. I consider nodemailer a good option. Here is the code that generates the password and updates the user in the db. Which would be the best way to set this up? thx!
const attemptResetPasswordMember = async (req, res) => {
try {
const genPass = await generatePassword();
const hashedPass = await bcrypt.hash(genPass, 10);
const { userid } = req.body;
await pool.query("Update members SET passhash = $1 WHERE userid = $2", [
hashedPass,
userid,
]);
res.status(200).json({ code: genPass });
} catch (error) {
res.status(500).json({ added: false });
}
};

Node.js, Mongoose: Cannot read properties of null (reading 'comparePassword')

I was attempting to updateUserPassword in my userController.js and came across the following error. Unsure what kind of error this could be. Any input and knowledge would be appreciated.
Getting this error on postman
{
"msg": "Cannot read properties of null (reading 'comparePassword')"
}
Body
{
"oldPassword":"secret",
"newPassword":"newsecret"
}
userController.js
const updateUserPassword = async (req, res) => {
const { oldPassword, newPassword } = req.body;
if (!oldPassword || !newPassword) {
throw new CustomError.BadRequestError('Please provide both values');
}
const user = await User.findOne({ _id: req.user.userId });
const isPasswordValid = await user.comparePassword(oldPassword);
if (!isPasswordValid) {
throw new CustomError.UnauthenticatedError('Invalid Credentials');
}
user.password = newPassword;
await user.save();
res.status(StatusCodes.OK).json({ msg: 'Success! Password Updated.' });
};
Github: https://github.com/k4u5hik/node-express-course
You need to check whether the user is exists or not before calling user.camparePassword, for example:
const user = await User.findOne({ _id: req.user.userId });
if (!user) {
throw new CustomError.UserNotFound();
}
const isPasswordValid = await user.comparePassword(oldPassword);

mongodb database fileds are not updating

Im trying to update the data , but its not updating in the database
but in console it is showing like this
Here is the code :
exports.forgotPassword = async( req, res) =>{
const { id, password } = req.body;
const user = await User.findOne({ _id : id })
if (!user) {
res.status(200).send({
message: "User was not found"
});
}
else {
const userUpdatedPassword = User.update({id}, {$set:{ password:encrypt(password),
updatedPasswordTime:new Date() }})
res.status(200).send({
message: "Upadted Sucessfully"
});
}
}
I think you put the wrong filter, you need to do const userUpdatedPassword = await User.updateOne({_id: id}, {$set:{ password:encrypt(password), updatedPasswordTime:new Date() }}), in addition you can await for result and print userUpdatedPassword to console to check if here updatedCount > 0.

TypeError: newUser.find is not a function

I am very new to the MERN stack and I would like some help figuring out this error. I'm trying to check if an email is already in the database upon creating a new user. Can anyone tell me why I am getting this error?
The model and scheme
//schema
const Schema = mongoose.Schema;
const VerificationSchema = new Schema({
FullName: String,
email: String,
password: String,
date: Date,
isVerified: Boolean,
});
// Model
const User = mongoose.model("Users", VerificationSchema);
module.exports = User;
The Api
const express = require("express");
const router = express.Router();
const User = require("../Models/User");
router.get("/VerifyEmail", (req, res) => {
console.log("Body:", req.body);
const data = req.body;
const newUser = new User();
newUser.find({ email: data.email }, function (err, newUser) {
if (err) console.log(err);
if (newUser) {
console.log("ErrorMessage: This email already exists");
} else {
console.log("This email is valid");
}
});
res.json({
msg: "We received your data!!!",
});
});
module.exports = router;
The api caller using axios
const isEmailValid = (value) => {
const info = {
email: value,
};
axios({
url: "http://localhost:3001/api/VerifyEmail",
method: "get",
data: info,
})
.then(() => {
console.log("Data has been sent");
console.log(info);
})
.catch(() => {
console.log("Internal server error");
});
};
if you have body in your request, change the type of request to POST...
after that for use find don't need to create a instance of model, use find with Model
router.get("/VerifyEmail", (req, res) => {
console.log("Body:", req.body);
const data = req.body;
User.find({ email: data.email }, function (err, newUser) {
if (err) console.log(err);
if (newUser) {
console.log("ErrorMessage: This email already exists");
} else {
console.log("This email is valid");
}
});
res.json({
msg: "We received your data!!!",
});
});
I prefer to use async/await and don't use Uppercase world for routing check the article: like this
router.post("/verify-email", async (req, res) => {
try {
let { email } = req.body;
let newUser = await User.findOne({ email });
if (newUser) {
console.log("ErrorMessage: This email already exists");
} else {
console.log("This email is valid");
}
} catch (error) {
res.json({
msg: "somthing went wrong",
});
}
res.json({
msg: "We received your data!!!",
});
});
The proper way to query a Model is like so:
const User = mongoose.model('Users');
User.find({<query>}, function (err, newUser) {...
So you need to get the model into a variable (in this case User) and then run the find function directly against it, as opposed to running it against an object you instantiate from it. So this is incorrect:
const newUser = new User();
newUser.find(...
So assuming all your files and modules are linked up correctly, this should work:
const User = require("../Models/User");
User.find({<query>}, function (err, newUser) {...
The problem wasn't actually the mongoose function but I needed to parse the object being sent.
let { email } = JSON.parse(req.body);
Before parsing the object looked like {"email" : "something#gmail.com"}
and after parsing the object looked like {email: 'something#gmail.com'}
I also changed the request from 'get' to 'post' and instead of creating a new instance of the model I simply used User.find() instead of newUser.find()

How to use async await with another API's implementation code

I have a user register function which is using async/await and I need to implement some code from another API. When I try to integrate it I get an error that I can't use await outside an async function.
exports.register = async (req, res) => {
// some logic here
nexmo.verify.request(
{
number: formattedMobile,
brand: "My Brand",
code_length: "4",
},
(err, result) => {
if (err) {
// If there was an error, return it to the client
return res.status(500).send(err.error_text);
}
// Otherwise, send back the request id. This data is integral to the next step
const requestId = result.request_id;
const salt = await bcrypt.genSalt(12);
const hashedPassword = await bcrypt.hash(password, salt);
const createdUser = new User({
name: name,
email: email,
mobile: formattedMobile,
password: hashedPassword,
});
try {
await createdUser.save();
res.status(200).send({ user: createdUser._id, otp: requestId });
} catch (err) {
res.status(500).send(err);
}
}
You need to make the callback function async, and most likely wrap the entire code in a try catch block to handle errors.
async (err, result) => {
if (err) {
// If there was an error, return it to the client
return res.status(500).send(err.error_text);
}
try {
// Otherwise, send back the request id. This data is integral to the next step
const requestId = result.request_id;
const salt = await bcrypt.genSalt(12);
const hashedPassword = await bcrypt.hash(password, salt);
const createdUser = new User({
name: name,
email: email,
mobile: formattedMobile,
password: hashedPassword,
});
try {
await createdUser.save();
res.status(200).send({ user: createdUser._id, otp: requestId });
} catch (err) {
res.status(500).send(err);
}
} catch(err) {
console.log(err);//do whatever error handling here
}
}

Resources