Nodemailer not working on Heroku deployment - javascript

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: 'js5360#columbia.edu',
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:)

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.

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.

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_ID=your#email.com
PASSWORD=yourpassword
ie key: EMAIL-ID
value: your#email.com
etc.
All the best

Related

In node.js, the post method is connected to get and a 404 error appears

node.js, if you change the 'post' method to 'get' in the client, it works well, but 404 error appears only in the 'post' method. May I know why?
P.S Many people say the problem is caused by the failure to find the path '/api/insert/' on the server, but I don't think it's because it works well when you change to the 'get' method.
client code
const writePost = async () => {
axios.defaults.withCredentials = true;
const config = {
headers: {
withCredentials: true,
},
body: {
title: writeData.title,
content: writeData.content,
register: writeData.register,
},
};
try {
//Successful response
await axios
.post("http://localhost:8000/api/insert", config)
.then((res) => {
console.log(res);
console.log(res.config);
});
} catch (error) {
//Failed to respond
console.log("write error", error);
}
};
node code
const cors = require("cors");
const express = require("express");
const app = express();
const mysql = require("mysql");
const PORT = process.env.port || 8000;
const bodyParser = require("body-parser");
const db = mysql.createPool({
host: "",
user: "",
password: "",
database: "",
});
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cors({ credentials: true, origin: true }));
app.post("/api/insert", (req, res) => {
var title = req.body.title;
var content = req.body.content;
var register = req.body.register;
const sqlQuery =
"INSERT INTO BOARD (BOARD_TITLE, BOARD_CONTENT, REGISTER_ID) VALUES (?,?,?);";
db.query(sqlQuery, [title, content, register], (err, result) => {
res.send(result);
});
});
I recreated your setup exactly (as described, adding writeData varible in client code, and app.listen(PORT) in server.js) and worked for me, the most possible cause is PORT is being defined to something else than 8000, it checks for process.env.port before hand, removing that fixes it.
Basically, if you enter the url path in your browser, it displays the 'get' method, so it is one-sided for the error to appear. To test the 'post' method, use the 'form' tag 'method' 'post', or the 'controller get post' or 'postman'.

how to get cookie in react passed from express js api (MERN stack)

I have an api in express js that stores token in cookie on the client-side (react). The cookie is generated only when the user logins into the site. For example, when I test the login api with the postman, the cookie is generated as expected like this:
But when I log in with react.js then no cookie is found in the browser. Looks like the cookie was not passed to the front end as the screenshot demonstrates below:
As we got an alert message this means express api is working perfectly without any error!!
Here is my index.js file on express js that includes cookie-parser middleware as well
require("dotenv").config();
const port = process.env.PORT || 5050;
const express = require("express");
const app = express();
const cors = require("cors");
const authRouter = require("./routes/auth");
var cookieParser = require('cookie-parser')
connect_db();
app.use(express.json());
app.use(cookieParser())
app.use(cors());
app.use("/" , authRouter);
app.listen(port , () => {
console.log("Server is running!!");
})
Code for setting up the cookie from express api only controller
const User = require("../models/user");
const jwt = require("jsonwebtoken");
const bcrypt = require('bcrypt')
const login = async (req, res) => {
const { email, password } = req.body;
try {
const checkDetails = await User.findOne({ email });
if (checkDetails) {
const { password: hashedPassword, token, username } = checkDetails;
bcrypt.compare(password, hashedPassword, function (err, matched) {
if (matched) {
res.cookie("token", token, { expires: new Date(Date.now() + (5 * 60000)) , httpOnly: true }).json({ "message": "You logged in sucessfully!" });
} else {
res.status(500).json({ "message": "Wrong password" });
}
});
} else {
res.status(500).json({ "message": "Wrong email" });
}
} catch (error) {
console.log(error.message);
}
}
Here is the react.js code that I am using to fetch data from api without using a proxy in package.json file
if (errors.length === 0) {
const isLogin = await fetch("http://localhost:5000/api/login", {
method: "POST",
body: JSON.stringify({ email, password }),
headers: {
"Content-Type": "application/json"
}
});
const res = await isLogin.json();
if(res) alert(res.message);
}
I want to get to know what is the reason behind this "getting cookie in postman but not in the browser". Do I need to use any react package?
The network tab screenshot might help you.
If I see in the network tab I get the same cookie, set among the other headers
To my understanding, fetch doesn't send requests with the cookies your browser has stored for that domain, and similarly, it doesn't store any cookies it receives in the response. This seems to be the expected behaviour of fetch.
To override this, try setting the credentials option when making the request, like so:
fetch(url, {
// ...
credentials: 'include'
})
or, alternatively:
fetch(url, {
// ...
credentials: 'same-origin'
})
You can read more about the differences between the two here.
I got my error resolved with two changings in my code
In front end just added credentials: 'include'
fetch(url, {
method : "POST"
body : body,
headers : headers,
credentials: 'include'
})
And in back end just replaced app.use(cors()); to
app.use(cors({ origin: 'http://localhost:3000', credentials: true, exposedHeaders: ['Set-Cookie', 'Date', 'ETag'] }))
That's it got resolved, Now I have cookies stored in my browser!!! Great. Thanks to this article:
https://www.anycodings.com/2022/01/react-app-express-server-set-cookie-not.html
during development i also faced same things, let me help you that how i solve it,
Firstly you use proxy in your react package.json, below private one:-
"private": true,
"proxy":"http://127.0.0.1:5000",
mention the same port on which your node server is running
Like:-
app.listen(5000,'127.0.0.1',()=>{
console.log('Server is Running');
});
above both must be on same , now react will run on port 3000 as usual but now we will create proxy to react So, react and node ports get connected on same with the help of proxy indirectly.
Now, when you will make GET or POST request from react then don't provide full URL, only provide the path on which you wants to get hit in backend and get response,
Example:-
React side on sending request, follow like this:-
const submitHandler=()=>{
axios.post('/api/loginuser',
{mobile:inputField.mobile,password:inputField.password})
.then((res)=>{
console.log(res);
})
.catch((err)=>{
console.log(err);
})
}
Node side where it will hit:-
app.post('/api/loginuser', async(req,res)=>{
//Your Code Stuff Here
res.send()
}
on both side same link should hit, it is very important
it will 100%.
don't forget to mention
on node main main where server is listening

React + Nodemailer + Express contact form: How do I get it working in Production?

I'm building a React website that features a simple contact form powered by Nodemailer and an Express backend. The folder structure of my project is as follows:
The form works as expected locally but not in Production. I figured some of my initial configuration would need to be updated, but it's been difficult to find a single source of truth - lots of tutorials on how to get such a form working locally, but minimal guidance beyond that.
In particular, these are the pieces that I know are going to need to be updated:
client/package.json: I've defined "proxy": "http://127.0.0.1:8080/"
client/src/components/Contact/Contact.js: In my submitRequest handler, I've also specified a localhost address in my axios post request
axios.post('http://localhost:8080/api', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
data
})
server/index.js
const express = require('express');
const path = require('path');
const cors = require('cors');
const nodemailer = require('nodemailer');
const dotenv = require('dotenv');
let app = express();
dotenv.config();
app.use(express.json());
app.options('*', cors());
app.get('/api', (req, res) => {
res.send('Welcome to the backend!')
})
app.use('/public', express.static(path.join(__dirname, 'public')));
let transporter = nodemailer.createTransport({
host: process.env.MAIL_HOST,
name: process.env.MAIL_HOST,
port: 465,
secure: true,
auth: {
user: process.env.MAIL_USER,
pass: process.env.MAIL_PASS
},
tls: { rejectUnauthorized: false },
logger: true
});
transporter.verify((error, success) => {
console.log(error ? error : 'Server is ready to take our messages');
});
app.post('/api', cors(), (req, res) => {
const name = req.body.data.name,
email = req.body.data.email,
message = req.body.data.message,
mail = {
from: name + ' <' + email + '>',
to: process.env.MAIL_TO,
subject: 'Contact Form Submission',
text: message
};
transporter.sendMail(mail, (err, data) => {
res.json({ status: err ? 'fail' : 'success' });
});
});
const PORT = process.env.PORT || 8080
app.listen(PORT, () => console.info(`server has started on ${PORT}`))
package.json (for server): an additional build will have to be added to scripts, one that links both client/ and server/. As of now, this is all I have:
"scripts": {
"server": "node server/index.js",
"client": "cd client && npm start",
"build:client": "cd client && npm run build",
"iall": "npm install && cd client && npm install",
"startboth": "npm run server && npm run client"
}
What research I've done seems to suggest I need to use Heroku or a similar service to run the backend in Production, but surely there's a way to have a functional contact form that doesn't require me to pay some kind of monthly fee (be it a PaaS like Heroku, or more streamlined solutions like SendGrid and EmailJS)?? If you can't already tell, backend isn't my forte. And so I would very much appreciate some guidance to get this form working, both locally and in Production.
FYI, the Production site can be found here (don't judge, this website is very much still in the skeleton phase). I can also provide a link to the Github repo if that would be helpful.

I'm trying to print form inputs data on the console of node.js, but the console is showing empty Curly braces

I'm trying to get form inputs data on the console of node.js, but, it's showing an empty Curly brace.
I used postman to check whether the data is received or not, it's working well, data has been received successfully on the terminal console.
<-------- I have taken the below steps to get the data from POSTMAN -------->
step-1: created a new collection and selected a post request
step-2: entered request URL
(http://localhost/loginandsingup)
step-3: in the header section, selected Content-Type in the key column and the value is application/json
step-4: added some data in the body section
{
"username": "shyam",
"email": "kumbamshyam955#gmai.com",
"mobilenumber": 6547896587,
"password": "superman145",
"confirmpassword": "superman145",
"checkbox": true
}
step-5: finally I clicked on the send button
Output from the postman, on terminal
[nodemon] restarting due to changes...
[nodemon] starting `node app.js`
Debugger listening on ws://127.0.0.1:61047/e3190b4a-b5a4-4806-9d45-c1f09c74212a
For help, see: https://nodejs.org/en/docs/inspector
Debugger attached.
application had started successfully on port 80
database conencted successfuly
{
username: 'shyam',
email: 'kumbamshyam955#gmai.com',
mobilenumber: 6547896587,
password: 'superman145',
confirmpassword: 'superman145',
checkbox: true
}
<-------- Code to get the form inputs data from locally hosted website -------->
// I used node.js for the backend //
app.js (Backend code)
const app = express();
const dotenv = require("dotenv");
const path = require('path');
dotenv.config({path: './config.env'})
const User = require('./model/userschema.js')
require('./db/database-connection.js')
const port = process.env.PORT
app.use(express.json());
app.use(require('./router/auth'))
const staticpath = path.join(__dirname, '/public');
app.use(express.static(staticpath));
app.set('view engine', 'pug');
app.set('views', path.join(__dirname, '/view'));
const middleware = (req, res, next)=>{
console.log("hello i'm middleware")
next();
}
app.get('/', (req, res) => {
res.status(200).render('home.pug');
})
app.get('/loginandsingup', (req, res) => {
res.status(200).render('loginandsingup.pug');
})
app.get('/customerservice', (req, res) => {
res.status(200).render('customerservice.pug');
})
app.get('/product', (req, res) => {
res.status(200).render('product.pug');
})
app.get('/404', (req, res) => {
res.status(200).render('404.pug')
})
app.use((req, res) => {
res.redirect('/404')
})
app.listen(port, () => {
console.log(`application had started successfully on port ${port}`)
})
router/auth.js (Backend code)
const express = require('express');
const router = express.Router();
require('../db/database-connection');
const User = require('../model/userschema');
router.post('/loginandsingup',(req, res) => {
const body = req.body
console.log(body);
res.json({message: req.body});
})
module.exports = router;
loginandsingup.pug (pug code)
doctype html
html(lang="en")
head
meta(charset="UTF-8")
//- meta(http-equiv='refresh' content='30')
meta(http-equiv="X-UA-Compatible", content="IE=edge")
meta(name="viewport", content="width=device-width, initial-scale=1.0")
title Mens Fashion | Login & singup
style
include ../public/css/login-and-singup-folder/loginstyle.css
include ../public/css/main-css/style.css
link(href="https://cdn.jsdelivr.net/npm/bootstrap#5.1.3/dist/css/bootstrap.min.css", rel="stylesheet",integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3", crossorigin="anonymous")
link(rel="preconnect", href="https://fonts.googleapis.com")
link(rel="preconnect", href="https://fonts.gstatic.com", crossorigin="")
link(href="https://fonts.googleapis.com/css2?family=Roboto&display=swap",rel="stylesheet")
body(style="background: rgba(236, 236, 236, 1)")
.loding-img-container
img#loading(src="images/Rolling scg.svg", alt="")
include navbar.pug
#login-and-singup-section.container
#login-and-singup-main-container.container.bg-light
#login-and-singup-container-1.login-and-singup-container.my-5
form#login-form
.mb-3
label.form-label(for='exampleInputEmail1') Email address
input#login-email.form-control(type='email' aria-describedby='emailHelp' autocomplete="off" placeholder="Enter Email")
.mb-3
label.form-label(for='exampleInputPassword1') Password
input#login-password.form-control(type='password' autocomplete="off" placeholder="Enter password")
button.btn.btn-success#login-submit-btn(type='submit') Sing in
button.btn.btn-outline-primary#logins-singup-btn(type='button') sing up
#login-and-singup-container-2.login-and-singup-container.my-5
.error-images.mb-3
img(src="images/error icon.png", alt="")
h1 Error !
span#error-msg Please Enter Corrext Email
form#singup-form(method='post' action='/loginandsingup')
.mb-3
label.form-label(for='username') User name
input#username.form-control(name='username' type='text' )
.mb-3
label.form-label(for='email') Email
input#email.form-control(name='email' type='Email')
.mb-3
label.form-label(for='mobilenumber') Enter number
input#mobilenumber.form-control(name='mobilenumber' type='number')
.mb-3
label.form-label(for='password') Enter New Password
input#password.form-control(name='password' type='password')
.mb-3
label.form-label(for='confirmpassword') Confirm Password
input#confirmpassword.form-control(name='confirmpassword' type='password')
.form-check.login-checkbox-container
input.bg-danger.border-danger#tandcCheckbox.form-check-input(name='tandcCheckbox' type='checkbox' checked)
label.form-check-label.m-0(for='tandcCheckbox') Agree To Our
a.text-danger Terms And Conditions
.form-check.login-checkbox-container
input.border-danger.form-check-input#upcoming-notification(name='offersmail' type='checkbox')
label.form-check-label.m-0(for='exampleCheck1') recieve upcomimg offers and events mails
button.btn.btn-success#new-user-submit-btn(type='submit') Submit
button.btn.btn-outline-primary#signups-login-btn(type='button') Login
script(type='text/javascript' src='js/loginandsingup.js')
script(
src="https://cdn.jsdelivr.net/npm/#popperjs/core#2.10.2/dist/umd/popper.min.js",
ntegrity="sha384-7+zCNj/IqJ95wo16oMtfsKbZ9ccEh31eOz1HGyDuCQ6wgnyJNSYdrPa03rtR1zdB",
crossorigin="anonymous"
)
script(
src="https://cdn.jsdelivr.net/npm/bootstrap#5.1.3/dist/js/bootstrap.min.js",
integrity="sha384-QJHtvGhmr9XOIpI6YVutG+2QOK9T+ZnN4kzFN1RtK3zEFEIsxhlmWl5/YESvpZ13",
crossorigin="anonymous"
)
Output from the locally hosted website, on terminal
[nodemon] restarting due to changes...
[nodemon] starting `node app.js`
Debugger listening on ws://127.0.0.1:55622/66b83ad8-4e15-4359-9d7b-1d638262c70a
For help, see: https://nodejs.org/en/docs/inspector
Debugger attached.
application had started successfully on port 80
database conencted successfuly
{}
**Any solution to this problem
By default forms submit as application/x-www-form-urlencoded enctype. NodeJs cant understand it by default so you need to install and configure body-parser module.
------->It's working✌️, after including the body-parser module
const express = require('express')
const router = express.Router()
var bodyParser = require('body-parser')
router.use(bodyParser.json());
router.use(bodyParser.json({ type: 'application/*+json' }))
var urlencodedParser = bodyParser.urlencoded({ extended: false })
router.post('/loginandsingup', urlencodedParser, (req, res) => {
const body = req.body;
console.log(body);
res.json({ message: req.body.username });
})
module.exports = router;

Error Connecting to the database: Error: ER_NOT_SUPPORTED_AUTH_MODE: Client does not support authentication protocol requested by server

I've tried following the suggestion at this link: MySQL 8.0 - Client does not support authentication protocol requested by server; consider upgrading MySQL client
and am aware that it is using cahcing_sha2_password instead of mysql_native. However, I've tried following the suggestions and am still receiving the same errors, trying to connect to the database with node.
Schema name: 'my_db'
//DATABASE CONNECTION SETTINGS
const APIServerPort = 3001;
const database = {
host: "localhost",
port: 3306,
user: "root",
password: "xxx",
database: "my_db"
};
module.exports = {
database,
APIServerPort
};
app.js file:
const express = require("express");
const app = express();
const router = express.Router();
const settings = require("./settings");
const routes = require("./routes");
const mysql = require("mysql");
const connection = mysql.createConnection(settings.database);
router.get("/employees", routes.employees.listAllEmployees);
app.use("/apis", router);
connection.connect(error => {
if (error) {
console.error("Error Connecting to the database: " + error);
return process.exit();
}
app.listen(settings.APIServerPort, () =>
console.info(`Server is running at port ${settings.APIServerPort}...`)
);
});
SQL Queries Ran:
CREATE USER 'root'#'localhost:3301/apis/employees' IDENTIFIED WITH mysql_native_password BY 'xxx';
FLUSH PRIVILEGES;
I'm still receiving the same error however.
mysql users are in the form 'root'#'localhost'. Urls are a node.js concept only.
As that user already exists create a new one:
CREATE USER myapplication#localhost
IDENTIFIED WITH mysql_native_password BY 'xxx';
GRANT ALL ON my_db.* TO myapplication#localhost`;
You don't need FLUSH PRIVILEGES.

Categories