Setting up Nodemailer Package

Series 5/5 of Building an Email Application using Node JS Express JS with Gmail and Nodemailer.

ยท

13 min read

Setting up Nodemailer Package

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 ๐Ÿ‘‡

image.png

This is where we are going to store our Environment-Specific variables in a NAME=value format like below ๐Ÿ‘‡

image.png

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 ๐Ÿ‘‡

image.png

Or like this ๐Ÿ‘‡

image.png

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 ๐Ÿ‘‡

image.png

Output ๐Ÿ‘‡

frame_generic_light (15).png

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 ๐Ÿ‘‡

standing clapping ovation


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.

  1. clientId : "your gmail client id",
  2. clientSecret : "your gmail client secret token",
  3. 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 ๐Ÿ‘‡

google cloud platform login to dashboard

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.

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.

Fill in a name for your project input box circled

  • After a few seconds delay your dashboard should look like this ๐Ÿ‘‡

frame_generic_light (20).png

  • After loading click on Go to APIs review ๐Ÿ‘‡

frame_generic_light (21).png

  • On the APIs review page, click on the OAuth consent screen option on the sidebar

frame_generic_light (22).png

  • Select external ๐Ÿ‘‡ and click on the create button

the OAuth consent Screen

  • 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.

basic information section

  • 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 ๐Ÿ‘‡.

the scope section is option

  • Add your email address as the test user and save ๐Ÿ‘‡

add test user section

  • And finally in the Summary section, review your application data and click on Back to dashboard when you are satisfied.

frame_generic_light (26).png


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 ๐Ÿ‘‡

frame_generic_light (29).png

- On the new page, click on Create Credentials and select the OAuth Client ID option from the dropdown ๐Ÿ‘‡

frame_generic_light (30).png

  • On the next page, select Web application option from the dropdown, since we are building our application for the web ๐Ÿ‘‡.

frame_generic_light (31).png

  • Choose a client name ๐Ÿ‘‡

frame_generic_light (32).png

frame_generic_light (34).png

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.

frame_generic_light (33).png

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 ๐Ÿ‘‡

frame_generic_light (35).png

  • 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

  1. Click on the settings (gear icon)
  2. Select the Use your own OAuth credentials
  3. Fill in your Client ID and Client Secret keys appropriately
  4. Find and click Gmail API v1 then select mail.googl.com from the dropdown

frame_generic_light (37).png

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 ๐Ÿ‘‡

frame_generic_light (38).png

If you are denied access, then you should check your test user in the Google Cloud Platform

  • Click on continue ๐Ÿ‘‡

frame_generic_light (39).png

  • Grand the application access to your Gmail account ๐Ÿ‘‡

frame_generic_light (40).png

And ๐Ÿ‘‡

frame_generic_light (41).png

  • You will be redirected back to the OAuth Playground, click on Exchange authorization code for tokens ๐Ÿ‘‡

frame_generic_light (42).png

  • Copy the refresh token and keep it safe ๐Ÿ‘‡

frame_generic_light (43).png

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 ๐Ÿ‘‡?

image.png

We can't always go to connect our application to the Google playground manually, this method here ๐Ÿ‘‡ is automatically doing that for us.

Connect to Google playground

2. Remember the refresh access token ๐Ÿ‘‡ also?

image.png

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.

Refresh Access Token.png

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 ๐Ÿ‘‡

frame_generic_light (48).png

  • Click on the send mail button, and if all goes well, you should be redirected to the Success Page ๐Ÿ‘‡.

frame_generic_light (52).png

  • Proceed to check your recipient mailbox, your sent e-mail should be received ๐Ÿ’ƒ.

frame_generic_light (51).png

  • Full mail body with attachment displayed ๐Ÿ‘‡

image.png


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.

giphy (6).gif

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.

  • Final Project Link:

  • Hosted Version Link:


giphy (5).gif


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 ๐Ÿ™‹โ€โ™‚๏ธ

image.png

If you found this helpful and want to support my blog, you can also buy me a coffee below.