I was creating a contact form using nodemailer. I implemented it and it was working fine, but some time later I came back to do a final test in postman it started to give me the following error"message": Not Found - /contact and "stack": "Error: Not Found - /contact\n . I am not sure what's causing the error.
this is code:
const transport = {
host: 'smtp.gmail.com', // Don’t forget to replace with the SMTP host of your provider
port: 587,
secure: false, // use SSL
auth: {
user: process.env.GMAIL,
pass: process.env.PASSWORD
}
}
const transporter = nodemailer.createTransport(transport)
transporter.verify((error, success) => {
if (error) {
console.log(error);
} else {
console.log('Server is ready to take messages');
}
});
app.post('/contact', (req, res, next) => {
const name = req.body.name
const email = req.body.email
const message = req.body.message
const content = ` name: ${name} \n email: ${email} \n message: ${message} `
const mail = {
from: name,
// to: 'RECEIVING_EMAIL_ADDRESS_GOES_HERE', // Change to email address that you want to receive messages on
to: 'xxx#gmail.com', // Change to email address that you want to receive messages on
subject: 'New Message from Contact Form',
text: content
}
transporter.sendMail(mail, (err, data) => {
if (err) {
res.json({
status: 'fail'
})
} else {
res.json({
status: 'success'
})
}
})
})
Related
I'm trying to find a way to set up a contact us form, when a user is submitting an enquiry I should receive an email, also the user should receive an email telling that the enquiry reached us, I figured out that I should use nodemailer to send mails, now the first step is done which I receive an email whenever the user submitted an enquiry, the question is how can I send him an email after submitting the enquiry? excuse my bad English
Nodemailer setup
//email from the contact form using the nodemailer
router.post('/send', async(req, res) => {
console.log(req.body);
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: process.env.EMAIL_USERNAME, //email resposible for sending message
pass: process.env.EMAIL_PASSWORD
}
});
const mailoptions = {
from: process.env.FROM_EMAIL,
to: process.env.EMAIL_USERNAME, //email that recives the message
subject: 'New Enquiry',
html: `<html><body><p>Name: ${req.body.name}</p><p> Email: ${req.body.email}</p><p>message: ${req.body.content}</p></body></html>`
}
transporter.sendMail(mailoptions, (err, info) => {
if (err) {
console.log('error');
res.send('message not sent'); //when its not ent
} else {
console.log('message sent' + info.response);
res.send('sent'); //when sent
}
})
});
module.exports = router;
So I tried some codes and code worked for me, it's sending to the admin and the client, hope it helps
router.post('/send', async(req, res, next) => {
console.log(req.body);
if (!req.body.name) {
return next(createError(400, "ERROR MESSAGE HERE"));
} else if (!req.body.email) {
return next(createError(400, "ERROR MESSAGE HERE"));
} else if (!req.body.email || !isEmail(req.body.email)) {
return next(createError(400, "ERROR MESSAGE HERE"));
} else if (!req.body.content) {
return next(createError(400, "ERROR MESSAGE HERE"));
}
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: process.env.EMAIL_USERNAME, //email resposible for sending message
pass: process.env.EMAIL_PASSWORD
}
});
const mailOptionsAdmin = {
from: process.env.FROM_EMAIL,
to: process.env.EMAIL_USERNAME, //email that recives the message
subject: 'New Enquiry',
html: `<html><body><p>Name: ${req.body.name}</p><p> Email: ${req.body.email}</p><p>message: ${req.body.content}</p></body></html>`
}
const mailOptionsClient = {
from: process.env.FROM_EMAIL,
to: req.body.email, //email that recives the message
subject: 'Recivied',
html: `<html><body><p>message: We recived your enquiry</p></body></html>`
}
transporter.sendMail(mailOptionsAdmin, (err, info) => {
if (err) {
console.log('error');
res.send('message not sent'); //when its not ent
} else {
console.log('message sent' + info.response);
res.send('sent'); //when sent
transporter.sendMail(mailOptionsClient)
}
})
});
I don't why i'm getting this every time I try to use Nodemailer to send emails:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent
to the client
at new NodeError (node:internal/errors:371:5)
at ServerResponse.setHeader (node:_http_outgoing:576:11)
the thing is it's working but i'm getting this error, by working I mean It sends the email.
The Code:
const sendEmail = async (email, subject, payload, template) => {
try {
// create reusable transporter object using the default SMTP transport
const transporter = nodemailer.createTransport({
service: "gmail",
auth: {
user: process.env.EMAIL_USERNAME,
pass: process.env.EMAIL_PASSWORD,
},
});
const options = () => {
return {
from: process.env.FROM_EMAIL,
to: email,
subject: subject,
html: template,
};
};
// Send email
transporter.sendMail(options(), (error, info) => {
if (error) {
return error;
} else {
return res.status(200).json({
success: true,
});
}
});
} catch (error) {
throw error;
}
};
router.post("/", async (req, res, next) => {
try {
if (!req.body.newsletterEmail) {
return next(createError(400, "Please enter your email"));
} else if (!req.body.newsletterEmail || !isEmail(req.body.newsletterEmail)) {
return next(createError(400, "Please enter a valid email address"));
}
const checkEmail = await SubscribedEmails.findOne({ newsletterEmail: req.body.newsletterEmail });
if (checkEmail) {
return next(createError(400, "Email is already subscribed"));
} else {
//create new user
const newSubscribedEmail = new SubscribedEmails ({
newsletterEmail: req.body.newsletterEmail,
});
//save user and respond
const SubscribedEmail = await newSubscribedEmail.save();
res.status(200).json({
message: "Successfully subscribed",
SubscribedEmail,
});
const link = `${process.env.CLIENT_URL}/`;
await sendEmail(
SubscribedEmail.newsletterEmail,
"Welcome to our newsletter!",
{
link: link,
},
`<div> <p>Hi,</p> <p>You are subscribed to our.</p> <p> Please click the link below to view more</p> <a href=${link}>GO</a> </div>`
);
return res.status(200).send(link);
}
} catch(err) {
next(err);
}
});
This is likely due to some quirks with using Gmail with Nodemailer. Review the docs here - https://nodemailer.com/usage/using-gmail/
and configure your Gmail account here -
https://myaccount.google.com/lesssecureapps
Finally, you may run into issues with Captcha. Configure here - https://accounts.google.com/DisplayUnlockCaptcha
I am really struggling with Nodemailer using Gmail in my NodeJS backend application. I have tried both OAuth and 2 factor authentication but am getting the following errors:
Either
Error Error: Mail command failed: 530-5.7.0 Authentication Required.
OR
Nodemailer Error: Missing credentials for "PLAIN"
Nodemailer set up is as below with OAuth when I am receiving the Authentication required error:
import nodemailer from "nodemailer";
interface mailOptions {
from: string;
to: string;
subject: string;
html: string;
}
const transporter = nodemailer.createTransport({
host: "smtp.gmail.com",
service: "gmail",
port: "587",
secure: false,
auth: {
type: "OAuth2",
user: 'EMAIL',
pass: 'EMAIL PASSWORD',
clientId: 'CLIENTID',
clientSecret: 'CLIENT SECRET',
refreshToken: 'OAUTH REFRESH TOKEN',
},
});
transporter.verify((err, success) => {
err
? console.log(err)
: console.log(`=== Server is ready to take messages: ${success} ===`);
});
export const sendMail = (mailOptions: mailOptions) =>
transporter.sendMail(mailOptions, function (err, data) {
if (err) {
console.log("Error " + err);
} else {
console.log("Email sent successfully");
}
});
The Missing credentials for "PLAIN" error is set up as below using 2 factor authentication:
import nodemailer from "nodemailer";
interface mailOptions {
from: string;
to: string;
subject: string;
html: string;
}
const transporter = nodemailer.createTransport({
service: "gmail",
auth: {
user: 'EMAIL',
pass: '2 FACTOR GENERATED PASSWORD FOR THE APP',
},
});
transporter.verify((err, success) => {
err
? console.log(err)
: console.log(`=== Server is ready to take messages: ${success} ===`);
});
export const sendMail = (mailOptions: mailOptions) =>
transporter.sendMail(mailOptions, function (err, data) {
if (err) {
console.log("Error " + err);
} else {
console.log("Email sent successfully");
}
});
controller code is as such:
ForgotPassword: async (req: Request, res: Response) => {
try {
const user = await userSchema.findOne({ email: req.body.email });
const username = user?.username;
if (user) {
const secret = process.env.ACCESS_TOKEN + user.password;
const payload = {
email: user.email,
id: user._id,
};
const token = jwt.sign(payload, secret, { expiresIn: "15m" });
const link = `http://localhost:${process.env.PORT}/user/reset-password/${user._id}/${token}`;
// console.log(link, token);
// Add in logic for sending the link via email to user here
const mailOptions = {
from: 'EMAIL',
to: 'EMAIL',
subject: "Password Reset",
html: "HTML",
};
sendMail(mailOptions);
res.status(200).send("Password reset link has been sent to your email");
} else {
res.status(404).send("User not found!");
}
} catch (e: unknown) {}
},
Edit:
The issue appears to be with loading environment variables, I added the email and generated password for 2 step verification into my app and it works perfectly. How to I get this to work with env variables?
After posting the edit, I ended up thinking that maybe I need to enable dotenv in my service file for the nodemailer. This worked perfectly. The following is the code I now have.
import nodemailer from "nodemailer";
import * as dotenv from "dotenv";
dotenv.config();
interface mailOptions {
from: string;
to: string;
subject: string;
html: string;
}
const transporter = nodemailer.createTransport({
service: "gmail",
auth: {
user: 'EMAIL',
pass: '2 FACTOR GENERATED PASSWORD FOR THE APP',
},
});
transporter.verify((err, success) => {
err
? console.log(err)
: console.log(`=== Server is ready to take messages: ${success} ===`);
});
export const sendMail = (mailOptions: mailOptions) =>
transporter.sendMail(mailOptions, function (err, data) {
if (err) {
console.log("Error " + err);
} else {
console.log("Email sent successfully");
}
});
i build full functions that worked and send emil to user when he "forgot password",its worked perfect on postMan, now the problem is when i clicked on the email url is direct me to "Cannot GET /resetpassword/eyJhbGciOiJIUzI1NiIsInR5cCeyJfaWQiOiI1ZmQ5ZmFmZjFjODA2YTgyMjZjMTQ5YmUiLCJp"
if i was do that for web app its work, but how can i redirect to open my app and show the screen for reset password?
exports.sendEmail = emailData => {
const transporter = nodeMailer.createTransport({
host: "smtp.gmail.com",
port: 587,
secure: false,
requireTLS: true,
auth: {
user: "roeigr7#gmail.com",
pass: "iaoqymixqarajkbv",
},
});
return transporter.sendMail(emailData);
};
and the data send to:
const emailData = {
from: "noreply#Lior.com",
to: email,
subject: "Password Reset Instructions",
text: `Please use the following link to reset your password: ${process.env.CLIENT_URL}/resetpassword/${token}`,
html: `<p>Please use the following link to reset your password:</p> <p>${process.env.CLIENT_URL}/resetpassword/${token}</p>`,
---------reset password FUNCTION:
exports.resetPassword = (req, res) => {
const { resetPassword, newPassword } = req.body;
User.findOne({ resetPassword }, (err, user) => {
// if err or no user
if (err || !user)
return res.status("401").json({
error: "Invalid Link!",
});
const updatedFields = {
password: newPassword,
resetPassword: "",
};
user = _.extend(user, updatedFields);
user.updated = Date.now();
user.save((err, result) => {
if (err) {
return res.status(400).json({
error: err,
});
}
res.json({
message: `סיסמא שונתה בהצלחה`,
});
});
});
};`
--------- resetPassword component:
const resetPassword = async resetInfo => {
try {
const response = await indexApi.put("resetPassword", {
resetInfo,
});
console.log("forgot password response: ", response);
} catch (err) {
console.log(err.response.data.error);
}
};
const resetPassModal = () => {
return (
<View style={{ marginVertical: 30 }}>
<Text onPress={resetPassword} style={styles.titletext}>
reset
</Text>
When I make a request to my endpoint I need to get a successful response only if the email is sent! otherwise, it should throw an error:
myendpoint.js
router.post("/", upload.none(), async (req, res) => {
try {
let body = JSON.parse(req.body.contact);
await getDb("messages").insertOne({
name: body.name,
email: body.email,
phone: body.phone,
subject: body.subject,
message: body.message,
});
await sendEmail(body);
res.send(
JSON.stringify({
success: true,
msg: "Message has been sent successfully",
})
);
} catch (err) {
res.send(JSON.stringify({ success: false, msg: err }));
}
});
sendEmail.js
const sendEmail = async function (props) {
const transporter = nodemailer.createTransport({
service: process.env.EMAIL_SERVICE,
host: process.env.EMAIL_HOST,
auth: {
user: process.env.EMAIL_FROM,
pass: process.env.EMAIL_PASS,
},
});
const mailOptions = {
from: process.env.EMAIL_FROM,
to: process.env.EMAIL_TO,
name: props.name,
email: props.email,
phone: props.phone,
subject: props.subject,
text: props.message,
};
transporter.sendMail(mailOptions, function (error, info) {
if (error) {
throw new Error("Message did Not send!");
}
});
};
the problem is before "await sendEmail(body);" ends and i get the error, i get the "Message has been sent successfully" and then server crashes!
what am i missing?
Check document function sendMail from nodemailer at here
If callback argument is not set then the method returns a Promise object. Nodemailer itself does not use Promises internally but it wraps the return into a Promise for convenience.
const sendEmail = async function (props) {
const transporter = nodemailer.createTransport({
service: process.env.EMAIL_SERVICE,
host: process.env.EMAIL_HOST,
auth: {
user: process.env.EMAIL_FROM,
pass: process.env.EMAIL_PASS,
},
});
const mailOptions = {
from: process.env.EMAIL_FROM,
to: process.env.EMAIL_TO,
name: props.name,
email: props.email,
phone: props.phone,
subject: props.subject,
text: props.message,
};
// remove callback and function sendMail will return a Promise
return transporter.sendMail(mailOptions);
};
Hello you can change your Transporter sendMail to :
return await transporter.sendMail(mailOptions);
Or You can Use Promise.
const handler = async ({ subject, name, body, contact }) => {
const transporter = nodemailer.createTransport({
service: "zoho",
// Disable MFA here to prevent authentication failed: https://accounts.zoho.com/home#multiTFA/modes
// ********************* OR *****************
// set up Application-Specific Passwords here: https://accounts.zoho.com/home#security/device_logins
auth: { user: process.env.NEXT_PUBLIC_EMAIL_ADDRESS, pass: process.env.NEXT_PUBLIC_EMAIL_PASSWORD },
});
return transporter
.sendMail(mailOptions({ subject, name, body, contact }))
.then((info) => {
if (process.env.NODE_ENV !== "production") console.log("Email sent: " + info.response);
return true;
})
.catch((err) => {
if (process.env.NODE_ENV !== "production") console.log("error sending mail", err);
return false;
});
};