Sending emails from a Node.js Application (and deploy it to Apache)
This article assumes you have Node.js up and running in your development computer. If not report to Installing Node.js via package manager as this is the easiest way to do it for various platforms.
In a terminal create your project folder by
mkdir myproject && cd myproject
Then create your package.json by
npm init -y
(the above flag -y skips all the questions)
For the purpose of sending emails we'll be using Express and NodeMailer which you can install by entering the following command in the terminal of your project folder. We'll also have to deal with parsing the request's body and further along the way with cors so we'll install four packages at once.
npm i express nodemailer body-parser cors
Fire up your favorite IDE/Text editor and create a app.js file in the project folder.
A barebone's express application would resemble the following:
var express = require('express');
var app = express();
const port = 3000;
app.get('/', function (req, res) {
res.send('Hello World!');
});
app.listen(port, function () {
console.log(`🚀 Server ready at http://localhost:${port}`);
});
You can check if it's running by issuing the following command at the terminal:
node app.js
Time to create the route and the transport for dealing with sending emails (in this case we're using the default SMTP transport for Gmail) so app.js becomes the following:
import express from "express";
import nodeMailer from 'nodemailer';
import bodyParser from 'body-parser';
const app = express();
const port = 3000;
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
app.post('/send-email', function (req, res) {
let transporter = nodeMailer.createTransport({
host: 'smtp.gmail.com',
port: 465,
secure: true,
auth: {
user: 'YOUR_USER@gmail.com',
pass: 'YOUR_PASSWORD'
}
});
let mailOptions = {
from: '"YOU" <YOUR_USER@gmail.com>', // sender address
to: req.body.to, // list of receivers
subject: req.body.subject, // Subject line
text: req.body.body, // plain text body
html: req.body.html // html body
};
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
res.sendStatus(500);
return console.log(error);
}
console.log('Message %s sent: %s', info.messageId, info.response);
res.sendStatus(200);
});
});
app.listen(port, function () {
console.log(`🚀 Server ready at http://localhost:${port}`);
});
At this point we can use Postman - Download Postman - to test the implementation (just replace _RECIPIENT_ with a valid account).
At this juncture you can either send the email successfully or fall victim to one of two scenarios:
- Gmail blocking it. You need to enable 'less secure apps" in your gmail account - Less Secure Apps
- CORS. That's what we'll be dealing with next
Note that the following is a very insecure configuration and should only be used for development purposes!
import express from "express";
import nodeMailer from 'nodemailer';
import bodyParser from 'body-parser';
import cors from 'cors';
const app = express();
const port = 3000;
// FIXME in production!
app.use(cors({
origin: '*',
credentials: true,
allowedHeaders: ['Content-Type', 'Authorization', 'Content-Length', 'X-Requested-With', 'Accept'],
methods: ['GET', 'PUT', 'POST', 'DELETE', 'OPTIONS'],
optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204
}));
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
app.post('/send-email', function (req, res) {
let transporter = nodeMailer.createTransport({
host: 'smtp.gmail.com',
port: 465,
secure: true,
auth: {
user: 'YOUR_USER@gmail.com',
pass: 'YOUR_PASSWORD'
}
});
let mailOptions = {
from: '"YOU" <YOUR_USER@gmail.com>', // sender address
to: req.body.to, // list of receivers
subject: req.body.subject, // Subject line
text: req.body.body, // plain text body
html: req.body.html // html body
};
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
res.sendStatus(500);
return console.log(error);
}
console.log('Message %s sent: %s', info.messageId, info.response);
res.sendStatus(200);
});
});
app.listen(port, function () {
console.log(`🚀 Server ready at http://localhost:${port}`);
});
That should take care of it! ;)
Client side methods for sending emails:
- via html FORM
- via axios (you need to npm install and import it to your app)
<form action="/send-email"> ... </form>
// Axios post example
axios.post('http://localhost:3000/send-email', {
to: _RECIPIENT_,
subject: "My exciting subject",
html: `<p>Hello World!</p>`
})
.then(res => {
console.log('Email sent.',res);
})
.catch(err => {
console.log('Failure to send email.', err);
})
Deployment on a Apache web server
(again this assumes you've installed and configured Apache on your computer)
In this scenario we have a client companion, say, React, application, also being served by express and we wish to deploy both to Apache.
// Example on how to serve the client application under express app.use(express.static(__dirname +'./../client/')); //serves the index.html
Our app current runs on port 3000 and we'd very much like to simply enter the URL without having to specify the port number, right? That's where Proxy comes into play:
You simply have to edit httpd.conf (check you platform's specific instructions for the location of this file) and in the <VirtualHost *:80> section write something like this:
<VirtualHost *:80>
ServerAdmin admin@site.com
ServerName site.com
ServerAlias www.site.com
ProxyRequests off
<Proxy *>
Order deny,allow
Allow from all
</Proxy>
<Location />
ProxyPass http://localhost:3000/
ProxyPassReverse http://localhost:3000/
</Location>
</VirtualHost>
WARNING: Do not enable proxying with ProxyRequests until you have secured your server. Open proxy servers are dangerous both to your network and to the Internet at large. Setting ProxyRequests to Off does not disable use of the ProxyPass directive.
We'll also need to enable proxy modules on Apache (example terminal command under Ubuntu) :
sudo a2enmod proxy proxy_http
Now restart apache server and test.
Lastly we'd also like to run our app after server reboots; We can achieve that with PM2 - Installation and usage
This concludes this article. Happy coding!
Feedback from my readers is what motivates me to keep working in public. Feel free to reach out, if you'd like to stay up to date with what I'm working on or simply present your own ideas! ;)
Mine doesn't work in production, even after adding cors