Next.JS and Nodemailer, Sending an email from contact form - javascript

I have a problem with my contact form in next.js, I don't have any errors (that are shown), everything worked untill deployment (on Vercel). I fetch my form, and have status 200, but I do not recive my email on Gmail. Also I don't recive any additional information.
I've recived emails, when I tested my app on "dev" and "build".
I've also have "less secure apps" option in Gmail account.
Here's my code in Next.JS:
fetch method in contact.js:
fetch("/api/contact", {
method: "POST",
headers: {
Accept: "application/json, text/plain, */*",
"Content-Type": "application/json",
},
body: JSON.stringify({
name: mailName,
email: mailAddress,
text: mailText,
}),
}).then((res) => {
console.log("Fetch: ", res);
res.status === 200
?
router.push("/success")
: router.push("/error");
in api/contact.js
require("dotenv").config();
const nodemailer = require("nodemailer");
export default (req, res) => {
const { name, email, text } = req.body;
const transporter = nodemailer.createTransport({
service: "gmail",
auth: {
user: process.env.EMAIL,
pass: process.env.PASSWORD,
},
});
const mailOption = {
from: `${email}`,
to: `${process.env.EMAIL}`,
subject: `New mail from ${email}`,
text: `
${name} wrote:
${text}
`,
};
transporter.sendMail(mailOption, (err, data) => {
if (err) {
console.log(err);
} else {
console.log("mail send");
}
});
console.log(name, email, text);
res.send("success");
};
Please help

Since your code runs fine in local and not in the deployment environment I have two suggestions.
First, make sure you have all the environment variables set.
Secondly, the way you have written your code it will always return success because transporter.sendMail is asynchronous and res.send is outside of it.
Change like,
transporter.sendMail(mailOption, (err, data) => {
if (err) {
console.log(err);
res.send("error" + JSON.stringify(err));
} else {
console.log("mail send");
res.send("success");
}
});

Related

POST to backend is returning a 404. (React => Express)

I've made a fairly simple register/login form for my backend to save to a database. It was working a couple days ago, and now nothing. When I make the fetch call the payload is filled out appropriately. My terminal tells me 'POST /api/v1/users/register 404 13.504 ms - 161'. My fetch request looks like:
const handleRegisterSubmit = async (e) => {
e.preventDefault()
await fetch('http://localhost:3333/api/v1/users/register', {
method: 'POST',
mode: 'cors',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({
dealershipname: registerForm.dealershipname,
password: registerForm.password,
streetadress: registerForm.streetadress,
city: registerForm.city,
state: registerForm.state,
zip: registerForm.zip,
phone: registerForm.phone,
email: registerForm.email
})
})
.then(res => res.json())
.then(data => {
if (data.error) {
setError(data.error)
} else {
navigate('/porsche')
}
})
}
My backend route looks like:
router.post('/register', async (req, res) => {
// check for username and password on req
if (!req.body.dealershipname || !req.body.password) {
return res.status(400).json({
error: 'Please include username and password'
})
}
// check database for existing user
const user = await models.User.findOne({
where: {
dealershipname: req.body.dealershipname
}
})
// if exists, send error
if (user) {
return res.json(400).json({
error: 'Username already in use. Pick another'
})
}
// hash password
const hash = await bcrypt.hash(req.body.password, 10)
// create user
const newUser = await models.User.create({
dealershipname: req.body.dealershipname,
password: hash,
streetadress: req.body.streetadress,
city: req.body.city,
state: req.body.state,
zip: Number(req.body.zip),
phone: Number(req.body.phone),
email: req.body.email
})
// respond with success message
return res.status(201).json(newUser)
})
My server is running fine. I've set the backend port to 3333 in case that it just needed some room from the front end port. The errors I'm getting in the browser are the same thing. Any help or suggestions would be a lifesaver.

Using nodemailer to send emails not working

I am creating a project with authentication and after registration this function is supposed to call
exports.sendEmail = async (req, res, next) => {
try {
const mailText = `<body style="text-align: center"><h1>Name Of Company</h1><p>This email was automatically sent to you to confirm your account at<b>Name of Company</b><br />Please press on this link to confirm your account</p></body>`;
const transporter = nodemailer.createTransport({
service: "gmail",
auth: {
user: "example#gmail.com",
pass: "password",
},
});
const options = {
from: "example#gmail.com",
to: "user#gmail.com",
subject: "Confirm Your Email",
html: mailText,
};
transporter.sendMail(options, function (err, info) {
if (err) {
console.log(err);
return res.status(500).json({
err: err,
});
}
return res.status(200).json({
success: true,
data: "Sent: " + info.response,
});
});
} catch (error) {
console.log("Error");
}
}
But the email is not sending and neither the try or catch are sending anything back.
I have also turned on the less secure apps in gmail
How do I fix this?

How to pass a file (PDF, DOC, etc) from Client (react) to server (Node - Express)

Client side
I use input file to get the PDF:
<html>
<body>
<input type="file" id="file_attachments" name="files" onChange=updateAttachments() multiple>
</input>
<script>
function updateAttachments() {
let files = document.getElementById("file_attachments").files;
console.log('attached', files);
}
</script>
</body>
As you can see from the above, I use .files.
I successfully update my react states with the files:
this.setState({
attachments: [...files]
});
Server side
I pass the files (attachments) to my server (node) via a fetch request (POST):
onSend = () => {
const url = config.API_ENDPOINT + "/api/email/send";
const email = {
to: this.state.emails,
subject: this.state.subject,
message: this.state.content,
attachments: this.state.attachments
};
fetch(url, {
method:"POST",
body: JSON.stringify(email),
headers: {
Accept: "application/pdf",
"Content-Type": "application/json"
}})
.then(res => {
if (!res.ok) {
res.json().then(error => {
throw error
})
}
return res.json()
})
.then(data => {
console.log('sent complete');
console.log(data);
})
.catch(err => {
this.setState({
error: err.message
})
})
}
However, on the server side, I receive all the data, except attachments is empty:
emailRouter
.route('/send')
.post(jsonParser, (req, res, next) => {
console.log(req.body); //display what was received
let transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: 'myEmail#gmail.com', // generated ethereal user
pass: 'xxxxxx', // generated ethereal password
},
});
transporter.sendMail({
from: 'myEmail#gmail.com',
to: 'All of my Subscribers <myEmail#gmail.com>',
bcc: req.body.to,
subject: req.body.subject,
html: req.body.message,
attachments: req.body.attachments,
}, (err, info) => {
console.log('email sent or was there an error?');
if (err) {
console.log(err);
}
else {
console.log('Email Sent: ', info.response);
}
});
});
First, I'm aware that attachments: req.body.attachments wouldn't work for Nodemailer, I'll cross that bridge when I get there lol.
All the data received except for req.body.attachments?
This is what is logged by the console:
{
to: [email1#gmail.com, email2#yahoo.com, anotherOne#outlook.com],
subject: 'Test email',
message: '<p>This email was received from Nodemailer</p>\n',
attachments: {}
}
My question is this:
Why am I not receiving any file data when I clearly passed it through?
How do I properly pass a file from the front end to the server? If there is a better way please enlighten me. I've seen a lot of different techniques, but none have made complete sense to me yet.
To pass a file from the front end to the server Please use FormData (https://developer.mozilla.org/en-US/docs/Web/API/FormData) from client side. And server side I would recommend you to use the https://www.npmjs.com/package/express-fileupload package to handle incoming files
And dont forget to set your Content-Type header to multipart/form-data

How to add an event listener to trigger a server side email function?

So, I have an express application serving a static index.html page, and a function in my app.js file that sends an email when the server starts up.
What I'd like to do is send the email only when a user hits a 'submit' button on a form (and the form is successfully sent, so on success-confirmation, preferably).
How do I get my program to "listen" for an onClick / form-successfully-sent event and then run the server side code I have that sends an email?
const http = require('http');
const nodemailer = require('nodemailer');
const express = require('express');
const app = express();
const port = 8080;
app.listen(port, () => console.log(`App listening on port ${port}!`));
app.use(express.static('public'));
app.get('/', (req, res) => res.send('index.html'))
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: 'USERNAME#GMAIL.COM',
pass: 'PASSWORD'
}
});
const mailOptions = {
from: 'USERNAME#GMAIL.COM',
to: 'USERNAME2#GMAIL.COM',
subject: 'Sending Email using Node.js',
text: 'That was easy!'
};
transporter.sendMail(mailOptions, function(error, info) {
if (error) {
console.log(error);
} else {
console.log('Email sent: ' + info.response);
}
})
I would like to trigger the transporter.sendMail method/function when a user successfully submits a form.
Thanks!
Add a route on your server that will handle form submissions. There are multiple ways to do this, however, as a simple example, look at the code below:
app.post("/send", function(req, res, next) {
const transporter = nodemailer.createTransport({
service: "gmail",
auth: {
user: "test-email#gmail.com",
pass: "test123"
}
});
const mailOptions = {
from: `${req.body.email}`,
to: "test-email#gmail.com",
subject: `${req.body.name}`,
text: `${req.body.message}`,
replyTo: `${req.body.email}`
};
transporter.sendMail(mailOptions, function(err, res) {
if (err) {
console.error("there was an error: ", err);
} else {
console.log("here is the res: ", res);
}
});
});
Then, in your client application, call the function below (or something similar) to send the client-side data to the newly-created endpoint:
function sendEmail(name, email, message) {
const options = {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify({
name: name,
email: email,
message: message
})
};
return fetch("/send", options)
.then(res => res.json())
.then(res => {
console.log("here is the response: ", res);
})
.catch(err => {
console.error("here is the error: ", err);
});
}
You'll have to set up an API endpoint
app.post('/send-mail', function (req, res) {
transporter.sendMail(mailOptions, function(error, info) {
if (error) {
console.log(error);
} else {
console.log('Email sent: ' + info.response);
}
})
})
And then call that from your form code.
onSubmit() {
fetch('/send-mail', {
method: 'POST'
})
}
Edit: typo.
Put this part inside a function and call it when you handle your form request, create parameters in function as needed for f.ex. mailOptions information
function sendEmail(){
const mailOptions = {
from: 'USERNAME#GMAIL.COM',
to: 'USERNAME2#GMAIL.COM',
subject: 'Sending Email using Node.js',
text: 'That was easy!'
};
transporter.sendMail(mailOptions, function(error, info) {
if (error) {
console.log(error);
} else {
console.log('Email sent: ' + info.response);
}
})
}
const bodyparser = require("body-parser");
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: `${req.body.username}`,
pass: `${req.body.password}`
}
});
const mailOptions = {
from: `${req.body.email}`,
to: 'USERNAME2#GMAIL.COM',
subject: ``${req.body.subject},
text: `${req.body.message}`
};
app.post("/login", (req, res)=>{
transporter.sendMail(mailOptions, function(error, info) {
if (error) {
console.log(error);
} else {
console.log('Email sent: ' + info.response);
}
})
})
remember to do npm install --save body-parser and also app.use(bodyparser.json())

Calling NodeJS function from JS function

I am doing a project based on firebase and I need to link a server-side function that sends an email to the client-side script.
This is my server-side index.js file
const functions = require('firebase-functions');
var nodemailer = require('nodemailer');
var transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: 'xxx#gmail.com',
pass: 'password'
}
});
var mailOptions = {
from: 'xxx#gmail.com',
to: 'xxx#gmail.com',
subject: 'Sending Email using Node.js',
text: 'That was easy!'
};
transporter.sendMail(mailOptions, function(error, info){
if (error) {
console.log(error);
} else {
console.log('Email sent: ' + info.response);
}
});
I am wondering how I could have a button in html call a function in the script that will call the transporter.sendMail. I have never touched node js before so please excuse my lack of knowledge.
If this helps firebase setup my folders to be separated by functions and public for the server-side and client-side files
First initialize your HTML page with jQuery and on submitting the form send an Ajax request to the server as follows
$(document).ready(function() {
$("#formoid").submit(function(event) {
event.preventDefault();
$.ajax({
url: 'http://xxxxxxx.com/contact', // url where to submit the request
type : "POST", // type of action POST || GET
dataType : 'json', // data type
data : $("#formoid").serialize(), // post data || get data
success : function(result) {
$('#formoid')[0].reset();
},
error: function(xhr, resp, text) {
console.log(xhr, resp, text);
}
})
});
});
Create a route called contact in your NodeJS server and listen for the contact request with All parameters required for your need. In the following case am using an express server and body parser to parse the data from incoming request
app.post('/contact', (req, res) => {
var transporter = nodemailer.createTransport({
service: "Gmail",
auth: {
user: "xxxxx",
pass: "xxxxx"
}
});
var mailOptions = {
from: req.body.email,
to: 'xxxxx#xx.com',
subject: 'Contact form',
text: 'From: ' + req.body.name + '\n Email: ' + req.body.email + '\nMessage: ' + req.body.msg
};
transporter.sendMail(mailOptions, function (error, info) {
if (error) {
res.status(500).json({
message: "Error",
error: error
})
} else {
res.status(200).json({
message: "Its working",
response: info.response
})
}
});
});
In the above request am sending the name:as name, email: as email and message: as msg

Categories

Resources