Reputation: 231
So I deployed my website portfolio with Heroku but my contact form (which uses nodemailer) is not working. It's weird because when I try it from my computer, I receive an email but I hear from others that it is not working on their end. This is the code of my index.js:
var express = require('express');
var router = express.Router();
var nodemailer = require('nodemailer');
var cors = require('cors');
const creds = require('./config');
var transport = {
host: 'smtp.gmail.com',
port: 465,
auth: {
user: creds.USER,
pass: creds.PASS
}
}
var transporter = nodemailer.createTransport(transport)
transporter.verify((error, success) => {
if (error) {
console.log(error);
} else {
console.log('Server is ready to take messages');
}
});
router.post('/send', (req, res, next) => {
var name = req.body.name
var email = req.body.email
var message = req.body.message
var content = ` name: ${name} \n email: ${email} \n message: ${message} `
var mail = {
from: name,
to: '[email protected]',
subject: 'New Message from Contact Form',
text: content
}
transporter.sendMail(mail, (err, data) => {
if (err) {
res.json({
status: 'fail'
})
} else {
res.json({
status: 'success'
})
}
})
})
const app = express()
app.use(cors())
app.use(express.json())
app.use('/', router)
app.listen(3002)
Here's the handler function I used:
constructor(props) {
super(props);
this.state = {
name: "",
email: "",
message: "",
}
}
handleSubmit(e){
e.preventDefault();
axios({
method: "POST",
url:"http://localhost:3002/send",
data: this.state
}).then((response)=>{
if (response.data.status === 'success'){
alert("Message Sent.");
this.resetForm()
}else if(response.data.status === 'fail'){
alert("Message failed to send.")
}
})
}
resetForm(){
this.setState({name: "", email: "", message: ""})
}
Previously when I was working on localhost, I had already enabled access to less secure apps so it was working fine locally.
Admittingly, I don't know much about express or nodemailer and followed the instructions outlined here: https://blog.mailtrap.io/react-contact-form/. I have a feeling that the url I am sending get/post requests is a local one, which makes prevents it from working on computers other than my own.
Could anyone provide some input into what I have to fix? Any help would be greatly appreciated:)
Upvotes: 1
Views: 2859
Reputation: 47
I was searching a similar issue. When I send contact mail from my local backend server port 3000, it worked fine. But after pushing to Heroku it didn't work. The issue was related to the .env file.
So by adding the ".env" codes directly to "Config Vars" in the settings panel of the Heroku dashboard, I was able to successfully send an email.
[email protected]
PASSWORD=yourpassword
ie key: EMAIL-ID value: [email protected] etc.
All the best
Upvotes: 1
Reputation: 796
Try this,
https://accounts.google.com/b/0/DisplayUnlockCaptcha
I received an email by Google that my account was being accessed from the server location, if you do just tell Google not to block it.
This will allow machines to access your Gmail remotely.
Note:This will work for a short period of time.
Upvotes: 2
Reputation: 125
I'm actually having this same issue right now but there are a few things that you might need to fix prior to getting to that point, the big one is environment variables.
Is your page deploying to heroku still despite being served on port 3002? Like your content is showing up after Heroku builds it? I had that error and if you're facing it you can solve it by replacing server port with process.env.PORT, which heroku automatically will assign internally during produciton. See the server file I've pasted below.
const port = process.env.PORT || 3000;
...
app.listen(port, () => {
console.log(`Server is up on port ${port}!`);
});
will take care of that.
Secondly, the when deployed the URL isn't going to be localhost anymore. it has to point to your domain suffixed by the api route axios.post('www.example.com/send', object)...
you can have that taken care of during the build by using dotenv environment variables ex:
let URI = process.env.NODE_ENV === 'development' ? process.env.REACT_APP_DEV_URI : process.env.REACT_APP_PROD_URI;
and having a .env file in your root directory
REACT_APP_DEV_URI=http://www.localhost:3000
REACT_APP_PROD_URI=https://www.example.com
then in your react client
axios.post(`${URI}/send-email`, data)
look up the dotenv npm module and using it to pull different environment variables based on if the app is in dev or production mode. via process.env.NODE_ENV
best practices is to .gitignore this file as it an expose credentials in public on github. Heroku will ignore it anyways, environment variables have to be set via the CLI, you can just copy and paste each on in one at a time and set with these commands or there is a page on heroku for adding this config. https://devcenter.heroku.com/articles/config-vars
same thing on the server side.
/**
* express.router() option? : https://stackoverflow.com/questions/61852261/nodemailer-not-working-on-heroku-deployment
*/
const express =
require('express'),
bodyParser = require('body-parser'),
nodemailer = require('nodemailer'),
cors = require('cors'), path = require('path'),
port = process.env.PORT || 3000;
require('dotenv').config();
let directory = process.env.NODE_ENV === 'development' ? 'public' : 'build',
publicPath = path.join(__dirname, '..', directory);
const app = express();
console.log(process.env.NODE_ENV)
app.use(cors());
app.use(express.static(publicPath));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.get('*', (req, res) => {
res.sendFile(path.join(publicPath, 'index.html'));
});
app.post('/send-email', (req, res) => {
console.log('request: ', req.body)
let data = req.body;
let transporter = nodemailer.createTransport({
service: 'gmail',
port: 465,
auth: {
user: process.env.EMAIL,
pass: process.env.PASSWORD
}
});
let mailOptions = {
from: data.email,
to: process.env.EMAIL,
subject: `${data.subject}`,
html: `<p>${data.name}</p>
<p>${data.email}</p>
<p>${data.message}</p>`
};
// console.log('mailOptions are', mailOptions)
transporter.sendMail(mailOptions,[
(err, info) => {
if(err) {
res.send(err)
} else {
res.send(info.messageId)
}
transporter.close();
}]);
})
app.listen(port, () => {
console.log(`Server is up on port ${port}!`);
});
I'll keep you updated as to what I'm doing to get mine to work as well.
Upvotes: 1