Setting up Nodemailer Package
Series 5/5 of Building an Email Application using Node JS Express JS with Gmail and Nodemailer.
This is the final article of the Building an Email Application using Node JS Express JS with Gmail and Nodemailer. You can check out the fourth series Here where we handled file upload from the HTML form in our project Node JS application.
Now that we can get data submitted from our HTML form to our Node JS server, let us proceed to set up the logic that will actually send out the mail ๐ to our recipient.
In other to achieve this, we need to install and setup the awaited Nodemailer
package, just as the name implies, this package allows us to send mail from our Node js application without stress, to begin with, let us install the Nodemailer
package using the code below.
yarn add nodemailer
Nodemailer Requirements
Now that the nodemailer
package has been successfully installed, let us break down the requirements for using the nodemailer
package.
1. Nodemailer requires a transporter object
This is where we configure the means of sending out the mail, we are going to be using the Gmail service in this article.
Below ๐ is the format of a nodemailer
transport.
let transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
type: 'OAuth2',
user: "your gmail address",
pass: "your gmail password",
clientId: "your gmail client id",
clientSecret: "your gmail client secret token",
refreshToken: "gmail refresh token",
}
});
2. Nodemailer requires a mailOption
This is an object where we specify the following
a. The sender's email address (required)
b. The recipient's email address (required)
c. The mail subject (optional)
d. The email text body (optional) and
e. The attachments (optional)
Below ๐ is the format for a mailOption
let mailOptions = {
from: "your gmail address",
to: "your recipient email address",
subject: "e-mail subject",
text: "e-mail body",
};
3. Node mailer requires a sendMail
method
Nodemailer sendMail
method is actually the one responsible for the sending of email to the recipient, this method takes in the mailOption
we've discussed above and also a callback function to handle the status of the mail transportation.
Below ๐ is the format for sendMail
method
transporter.sendMail(mailOptions, function(err, data) {
if (err) {
console.log("Error: " + err);
} else {
console.log("Email sent successfully");
}
});
The complete code of the nodemailer
should be like this ๐
// After the last console.log(attachmentPath) in the else statement
// Connecting to gmail service
let transporter = nodemailer.createTransport({
service: "gmail",
auth: {
type: "OAuth2",
user: "your gmail address",
pass: "your gmail password",
clientId: "your gmail client id",
clientSecret: "your gmail client secret token",
refreshToken: "gmail refresh token",
},
});
// e-mail option
let mailOptions = {
from: "your gmail address",
to: "your recipient email address",
subject: "e-mail subject",
text: "e-mail body",
};
// Method to send e-mail out
transporter.sendMail(mailOptions, function (err, data) {
if (err) {
console.log("Error: " + err);
} else {
console.log("Email sent successfully");
}
});
The above code will serve as our nodemailer template, which we need to find the credentials to, but before we do that, we need to set up a secure way to protect the transporter credentials such as the user, pass, clientId, clientSecret and the refresh token that we be provided by google, so that everyone will not be able to see our application secret details from Github repository when we finally push to production.
Setting up dotenv
dotenv
allows us to keep secret credentials or tokens in our project environment (.env file), which will not be visible to either the browser, git version control, or people viewing our project on Github, these are called the Environment Variables
.
Ensure to add the .env file to your .gitignore file
Run the code below ๐ to install the dotenv
to your project
yarn add dotenv
After the dotenv
installation, proceed to create a .env
file in your project root directory like this ๐
This is where we are going to store our Environment-Specific variables in a NAME=value
format like below ๐
To access this enviroment-variable from our JavaScript application we need to require (import) the dotenv
package in our index.js
and configure it like below ๐
Or like this ๐
You can use this method of import when you need to use other dotenv
function like parse
, but in this article, we are going to make use of the first method ๐ because we only need the config()
function.
To learn more about the dotenv
package, you can visit the official documentation Here
Accessing our environment-variables from dotenv
file
Now that we know how to use the dotenv
package to store our enviroment-variable in our JavaScript application let us see how we can then access this variables in our scripts.
Add this line of code below the dotenv
configuration ๐
Output ๐
And that is totally how to set up and use environment-variables with dotenv
package in a JavaScript project (Not only in Node JS) ๐.
If you made it this far, you deserve this ๐
FINAL LAP
Gmail OAuth2 Configuration
Now that we have everything setup in our Node JS application, let's proceed to get the following credentials from Google.
clientId
: "your gmail client id",clientSecret
: "your gmail client secret token",refreshToken
: "gmail refresh token",
Follow the guide below to setup you Gmail OAuth 2 Configurations
1. Google Cloud Platform Account Setup
Firstly we need to register our Gmail address on the Google Cloud Platform , if you have not done that before you can register HERE.
If your gmail account is logged in on your browser you should have a screen display like this ๐
Accept the Terms and Condition by checking the box, the Email Updates is optional, and then click on Accept and Continue
2. Setting up a New Project
Next, we need to create a new project, to do this click on the CREATE PROJECT link.
- Setup Project Name
Fill in a name for your project, I am using projectmailSender
in this tutorial, note that this project name will be used to generate an id
for your project and this cannot be changed later.
- After you have decided on a project name, click on the Createbutton.
- After a few seconds delay your dashboard should look like this ๐
- After loading click on Go to APIs review ๐
- On the APIs review page, click on the OAuth consent screen option on the sidebar
On the OAuth consent Screen
- Select external ๐ and click on the create button
- The next step is to provide basic information about our app (nodemailSender) ๐
Only three inputs are compulsory, your app name and 2 emails, one for support and the other for contacting the developer.
Click save and continue when you are done.
- The scope section is optional, but if you want to fill it, you can.
I am going to skip it in this tutorial by clicking the Save and continue ๐.
- Add your email address as the test user and save ๐
- And finally in the Summary section, review your application data and click on Back to dashboard when you are satisfied.
Now that we have setup our new application, let us proceed to generate the credentials we are going to use in our Nodemailer.
- From the page we stopped earlier ๐, click on the Credentials option at the sidebar ๐
- On the new page, click on Create Credentials and select the OAuth Client ID option from the dropdown ๐
- On the next page, select Web application option from the dropdown, since we are building our application for the web ๐.
- Choose a client name ๐
- Scroll to the bottom of the page and add https://developers.google.com/oauthplayground to the Authorized redirect urls section ๐
After adding the URL, click on the Create button and if everything goes well, you should receive an alert that your OAuth credentials have been created, followed by this ๐ popup.
Copy your Client ID and your Client Secret key and keep them safe, because we will use it very soon.
At this point we have gotten 2 out of 3 credentials that we need to configure out nodemailer, the last credential we are looking for is the refreshToken, let's go it em ๐ฃ.
Generating OAuth Refresh Token
We will be using the Client ID and the Client Secret to generate our refresh token.
(I hope you saved it earlier?)
If you, you can retrieve it from the credential tab ๐
OAuth Playground
Remember that we set the Authorized redirect urls to https://developers.google.com/oauthplayground ?
Now let's head to the playground to grab our refresh token ๐
click here ๐ https://developers.google.com/oauthplayground
On the OAuth Playground
- Click on the settings (gear icon)
- Select the Use your own OAuth credentials
- Fill in your Client ID and Client Secret keys appropriately
- Find and click Gmail API v1 then select mail.googl.com from the dropdown
Click on the Authorize APIs button.
- You should be redirected to the page below, ensure to select the email address you added as a Test user ๐
If you are denied access, then you should check your test user in the Google Cloud Platform
- Click on continue ๐
- Grand the application access to your Gmail account ๐
And ๐
- You will be redirected back to the OAuth Playground, click on Exchange authorization code for tokens ๐
- Copy the refresh token and keep it safe ๐
Setting up Google Transporter
Now that we have gotten our required credentials, let's update our nodemailer logic with the googleapis
package.
install the google API using the code below
yarn add googleapis
What we want to achieve below is to connect to our Google Playground and create a new token each time we need to send a mail and to achieve this, we need to dedicate a function named createTransport to connect to the playground and always create a new access token when we try to send a new email, with this we will never encounter expired token error.
The createTransport
function ๐
// Googleapis
const { google } = require("googleapis");
// Pull out OAuth2 from googleapis
const OAuth2 = google.auth.OAuth2;
const createTransporter = async () => {
// 1
const oauth2Client = new OAuth2(
process.env.OAUTH_CLIENT_ID,
process.env.OAUTH_CLIENT_SECRET,
"https://developers.google.com/oauthplayground"
);
// 2
oauth2Client.setCredentials({
refresh_token: process.env.OAUTH_REFRESH_TOKEN,
});
const accessToken = await new Promise((resolve, reject) => {
oauth2Client.getAccessToken((err, token) => {
if (err) {
reject("Failed to create access token :( " + err);
}
resolve(token);
});
});
// 3
const transporter = nodemailer.createTransport({
service: "gmail",
auth: {
type: "OAuth2",
user: process.env.SENDER_EMAIL,
accessToken,
clientId: process.env.OAUTH_CLIENT_ID,
clientSecret: process.env.OAUTH_CLIENT_SECRET,
refreshToken: process.env.OAUTH_REFRESH_TOKEN,
},
});
// 4
return transporter;
};
Explanation
After importing the googleapis
and pulling out OAuth2
, let's see what we have in the createTransporter
function.
1. Remember this ๐?
We can't always go to connect our application to the Google playground manually, this method here ๐ is automatically doing that for us.
2. Remember the refresh access token ๐ also?
The method below will automatically do that for us in other to keep the access token active, it requires the refresh token to generate a new access token, that is why we are passing the token from our environment-variable along with it.
3. We are authenticating ourselves as the owner of the Gmail account we want to use as the transport.
4. we are returning the response from the transport authentication (approved or declined)
Now that we have an understanding of what is going on, let us update our mailOptions and our sendMail method ๐
// Route to handle sending mails
app.post("/send_email", (req, res) => {
attachmentUpload(req, res, async function (error) {
if (error) {
return res.send("Error uploading file");
} else {
// Pulling out the form data from the request body
const recipient = req.body.email;
const mailSubject = req.body.subject;
const mailBody = req.body.message;
const attachmentPath = req.file?.path;
// Mail options
let mailOptions = {
from: process.env.SENDER_EMAIL,
to: recipient,
subject: mailSubject,
text: mailBody,
attachments: [
{
path: attachmentPath,
},
],
};
try {
// Get response from the createTransport
let emailTransporter = await createTransporter();
// Send email
emailTransporter.sendMail(mailOptions, function (error, info) {
if (error) {
// failed block
console.log(error);
} else {
// Success block
console.log("Email sent: " + info.response);
return res.redirect("/success.html");
}
});
} catch (error) {
return console.log(error);
}
}
});
});
What we are doing above is to create an asynchronous function without our post route, because we are going to be waiting for a response from the createTransportorer
function, we also created a new attachment key in our mailOpitons which will hold our attachment.
The try block is to catch any error that is going to occur during the process of connecting to the Google playground and sending out emails.
The if statement which serves as a conditional statement will check if there is an error and log it out, but if there is no error during the process, it will redirect to the success page.
Now let us test our code ๐
Because this article is long already, I have created a Github Gist Here
Update your index.js
file with the code from the Git ๐ and fill the form in your browser with an attachment, like below ๐
- Click on the send mail button, and if all goes well, you should be redirected to the Success Page ๐.
- Proceed to check your recipient mailbox, your sent e-mail should be received ๐.
- Full mail body with attachment displayed ๐
Cleaning Up
Now that our project is complete, there is one important thing to do, which is deleting the attachments from our server document, everytime a mail has been sent, we don't want to keep the user's files, and these files can also take up huge space.
To achieve this, we need to install a package called fs
๐
yarn add fs
The fs package allows us to perform actions with our computer files through Node JS more info HERE
Finally, let us update our sendMail
method within the else success block with the code below ๐
// Send email
emailTransporter.sendMail(mailOptions, function (error, info) {
if (error) {
// failed block
console.log(error);
} else {
// Success block
console.log("Email sent: " + info.response);
fs.unlink(attachmentPath, function (err) {
if (err) {
return res.end(err);
} else {
console.log(attachmentPath + " has been deleted");
return res.redirect("/success.html");
}
});
}
});
The updated Gist for index.js
to this point is Here
Now try to send another mail with an attachment to test if the sent files still remain in the folder, remember to manually remove the existing ones.
And that is how to implement a mailing application into your NODE JS project.
In this article, you just learned the basics of Node JS which includes Routing, rendering HTML files, integrating packages, working with Google APIs, and file handling to mention a few. This should get you started in using these technologies to build more projects on your own.
Useful Links ๐
Final Project Link:
Hosted Version Link:
Wow, what a journey, I am glad you made it to the end of this article series, if you enjoyed and learned from this article, I will like to connect with you, so you won't miss any of my upcoming articles like this.
Let's connect on
See you in the next article. Bye Bye ๐โโ๏ธ
If you found this helpful and want to support my blog, you can also buy me a coffee below.