ExpressJS/Mongoose Set variable to MongoDB result - node.js

I have a NodeJS app using Express and Mongoose. I am trying to write a POST route so that when a POST request to the URL is made (currently /api/v1/forms/:formId) then it will set the variable to the recipient`value from the MongoDB database. The :formID will match the _id in MongoDB.
So far I have:
app.post("/api/v1/forms/:formId", async (req, res) => {
//TODO: Create Mailer and email templates before finalising route.
const { _name, _email, _message } = req.body;
const form = Form({
name = req.body._name,
email = req.body._email,
message = req.body._message,
recipient = Form.findById(req.params.form_id, function(err, form) {
if (err)
res.send(err);
})
});
const mailer = new Mailer(form, contactFormTemplate(form));
try {
await mailer.send();
} catch (err) {
res.status(422).send(err);
}
});
I know this is not correct for the recipient field but this is what I could think of off the top of my head

Following what you did, you can do this:
app.post("/api/v1/forms/:formId", async (req, res) => {
//TODO: Create Mailer and email templates before finalising route.
const { _name, _email, _message } = req.body;
let recipient;
try{
recipient = await Form.findById(req.params.form_id).exec();
} catch(err){
res.status(422).send(err);
}
const form = Form({
name: req.body._name,
email: req.body._email,
message: req.body._message,
recipient: recipient
});
const mailer = new Mailer(form, contactFormTemplate(form));
try {
await mailer.send();
} catch (err) {
res.status(422).send(err);
}
});
The .exec() on mongoose return a promise, so it's possible to use async/await syntax.

As far as I understood the problem, here is what I could think of,
Form.findById(req.params.form_id, function(err, form) {
if (err){
res.send(err);
}else{
recipient = form.recipientValue;
}
});

Try this..
app.post("/api/v1/forms/:formId", async (req, res) => {
const { _name, _email, _message } = req.body;
let form;
try {
form = await Form.findById(req.params.formId).select('recipient').exec();
} catch (err) {
res.send(err);
return;
}
const nuForm = Form({
name = req.body._name,
email = req.body._email,
message = req.body._message,
recipient = form.recipient
});
try {
await new Mailer(nuForm, contactFromTemplate(nuForm)).send();
} catch(err) {
res.status(422).send(err);
}
}

Related

Send email through nodemailer nodejs

First I am checking the email present in the database, if present then it will generate the hashcode and after that it will update the database and once the hashcode is generated then email will be send to the user. So I am stuck how to send email please any help.
const md5 = require('md5');
let transporter = require("../config/transporter");
export const newemail = async(req,res ) => {
try{
var re = /^(([^<>()[\]\\.,;:\s#\"]+(\.[^<>()[\]\\.,;:\s#\"]+)*)|(\".+\"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
const user = await db.findOne('USER', { email: req.body.email });
res.status(200).json(user);
if (!re.test(req.body.email)) {
res.json('Email address is not valid!');
} else {
const email = req.body.email;
const name = email.substring(0, email.lastIndexOf("#"));
const domain = email.substring(email.lastIndexOf("#") +1);
const hashCode = md5(name + domain);
function sendingMail(referredBy){
const user = await db.findOneandUpdate('USER', { email: req.body.email, referralCode: hashCode, referredBy: referredBy });
const email = user.email;
console.log(email + "email");
//send verification mail
let mailOptions = {
from: 'xxyyzz#gmail.com', // sender address
to: email, // list of receivers
subject: settingsConfig.subjectLine, // Subject line
text: settingsConfig.message, // plaintext body
html: settingConfig.message // html body
};
console.log("MAILING!");
console.log(mailOptions)
// send mail with defined transport object
transporter.sendMail(mailOptions, function (error, info) {
if (error) {
return console.log(error);
}
console.log('Message sent: ' + info.response);
});
};
}
}
catch (err) {
console.log(err)
res.status(500).json({ message: "Something went wrong" });
}
}
Please anyone how to move further.
const md5 = require('md5');
let transporter = require("../config/transporter");
export const sendEmail = async (to, from, subject, body) => {
// use any mailer library to send the email
}
export const isValidEmail = async (email) => {
const regex = /^(([^<>()[\]\\.,;:\s#\"]+(\.[^<>()[\]\\.,;:\s#\"]+)*)|(\".+\"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return regex.test(email);
}
export const findUserByEmail = async (email) => {
// db instance
return await db.findOne('USER', { email });
}
export const generateHash = async (email) => {
const name = email.substring(0, email.lastIndexOf("#"));
const domain = email.substring(email.lastIndexOf("#") +1);
return md5(name + domain);
}
export const verifyAndEmail = async(req,res ) => {
try{
// var re = /^(([^<>()[\]\\.,;:\s#\"]+(\.[^<>()[\]\\.,;:\s#\"]+)*)|(\".+\"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
// const user = await db.findOne('USER', { email: req.body.email });
// res.status(200).json(user);
// step-1 destructure email from request body
const {
email
} = req.body;
// step-2, validate the email id
const isEmailValid = await isValidEmail(email);
if(!isEmailValid) {
throw new Error('Invalid email');
// return appropiate response
}
// step-3 find a user by email
const user = await findUserByEmail(email);
// step-4 validate, if user does not exists
if (!user) {
throw new Error('User does not exist');
// return appropiate response
}
//step-5 generate the hash using user's email
const hash = await generateHash(email);
// step-6, call send email method with the parameters
}
catch (err) {
console.log(err)
res.status(500).json({ message: "Something went wrong" });
}
}
I would suggest, you should refactor your code to following.
Run it and let me know if you face any issues.
This approach will help you to debug the error.

got `{}` when set a field in mongodb to `undefined` using mongoose

I'm facing a weird problem
I'm using mongoose 5.12.8, I try to delete a user's image with this route
router.delete('/users/me/avatar', auth, async(req, res) => {
try {
req.user.avatarObj = undefined
await req.user.save()
res.send({ message: "Deleted user's avatar" })
} catch (error) {
res.status(500).send(error.message)
}
});
but when I use get route, i got req.user.avatarObj is still an empty object {}
//get user avatar
router.get('/users/:id/avatar', async(req, res) => {
try {
const user = await User.findById(req.params.id);
console.log(user.avatarObj) // print {} ?????????
console.log(user) //user has no avatarObj field
if (user && user.avatarObj) {
res.set('Content-Type', user.avatarObj.contentType);
res.send(user.avatarObj.data);
} else {
throw new Error('Not found user or image');
}
} catch (err) {
res.status(404).send({ error: err.message });
}
});
this is my userSchema
avatarObj: {
data: Buffer,
contentType: String
}
and some middleware
//hash plain text password before saving
userSchema.pre('save', async function(next) {
const user = this
if (user.isModified('password')) {
user.password = await bcrypt.hash(user.password, 10)
}
next()
});
//remove all tasks before remove user
userSchema.pre('remove', async function(next) {
await Task.deleteMany({ owner: this._id })
next()
});
My question is: Why user.avatarObj is still an empty object?
Thank you guys!

How to pass data from express-validation middleware to next function?

I have an express app that looks like this.
const app = require('express')();
// Task model
const Task = require('./models/Task');
const { param, validationResult } = require('express-validator');
const getTaskValidations = [
param('id')
.custom(async (id, { req }) => {
try {
const task = await Task.findOne({ _id: id, user: req.user.id });
if (!task) Promise.reject('Task not found');
} catch (err) {
// Handle error
}
})
]
const validate = (req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(401).json({ message: errors.array()[0].msg });
}
next();
}
// Get Task by id
app.get('/tasks/:id', getTaskValidations, validate, async (req, res) => {
try {
const task = await Task.findById(req.params.id);
res.json(task)
} catch (err) {
// Handle err
}
})
I want to get the task by id. In GET tasks/:id the req.params.id will contain the id of the task.
The Task Model looks like this
{
id:
task:
user:
}
By looking at the endpoint it is clear that I'm passing two middlewares.
The first middleware getTaskValidations will check if the task will given id and req.user exists.
The second middleware validate will check for errors.
And then again will query database for task and send data to the client causing 2 database queries.
How can I reuse the same task obtained in the getTaskValidations middleware.
you can add result of query add to the req.body.task like this
const getTaskValidations = [
param('id')
.custom(async (id, { req }) => {
try {
const task = await Task.findOne({ _id: id, user: req.user.id });
req.body.task = task
if (!task) Promise.reject('Task not found');
} catch (err) {
// Handle error
}
})
]
in controller
app.get('/tasks/:id', getTaskValidations, validate, async (req, res) => {
try {
let {task} = req.body
res.json(task)
} catch (err) {
// Handle err
}
})
You can store the response you get from the query in the request(req).
const task = await Task.findOne({ _id: id, user: req.user.id });
if (task)
req.task = task;
else
Promise.reject('Task not found');
Later on, in the API endpoint, you can simply use
app.get('/tasks/:id', getTaskValidations, validate, async (req, res) => {
try {
const task = req.task;
res.json(task)
} catch (err) {
// Handle err
}
});
to get the task obtained in the getTaskValidations middleware.

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()

Expressjs and Body-parser Posting Articles

app.post("/article/add", function(req, res) {
let article = new Article();
article.title = req.body.title;
article.author = req.body.author;
article.body = req.body.body;
article.save(function(err) {
if (err) {
console.log(err);
return;
} else {
res.redirect("/");
}
});
});
How can I change this and add articles in a similar way. I mean I cant create other ways to make it but I know there are lots of ways to post articles.
If you mean, how to refactor your code in a better way, I would suggest these:
1-) use destructuring to parse req.body like this:
app.post("/article/add", function(req, res) {
const { title, author, body } = req.body;
let article = new Article({ title, author, body });
article.save(function(err) {
if (err) {
console.log(err);
return;
} else {
res.redirect("/");
}
});
});
2-) use async await syntax:
app.post("/article/add", async function(req, res) {
const { title, author, body } = req.body;
let article = new Article({ title, author, body });
try {
article = await article.save();
res.redirect("/");
} catch (err) {
console.log(err);
return;
}
});

Resources