Build an Email Application using Node JS Express JS with Gmail and Nodemailer - (All in one Article)
Published: 2021-06-18 07:37:05

Hello 😊, my gorgeous friend on the internet 👋, how are you doing today?.

Sometimes we want to send out a welcome email to our new registered users or send them a link to reset their forgotten password.


> This is an extracted feature from my HarperDB Hackerthon open source project, I found it difficult to implement, because of resources, so I am saving you from the stress I passed through.


Below you will learn how to send out emails directly from your Node JS Application using your registered Gmail account and Nodemailer, with an interface built with HTML and CSS that is powered by Express JS.

Don't worry about the complexity of what is listed above, I am going to explain all of them in this article (in a bit).


> You can check out the final project sample of what you will be building along with me in this article   [Here](https://mailing-application-project.herokuapp.com/).


If you would prefer a series of this article (broken into multiple parts), you can check it out  [HERE](https://unclebigbay.com/build-an-email-application-using-node-js-express-js-with-gmail-and-nodemailer-series-15)  

Note: Some of the block code on dev.to doesn't work well, you can check the canonical url, if you didn't get to copy the code from this article.


## PREREQUISITES

Before following along in this tutorial, I assume you already have a basic understanding of JavaScript  and that you already have the following setup:

1.  Gmail account

2.  Node installed on your PC

If you do not have a Gmail account setup, click [Here](http://gmail.com) to register your account and you can also download the Node application by clicking [Here](https://nodejs.org/en/).


> 
Before we proceed, kindly note that this is a very long article, because I ensure to explain every concept that is been used in building the application, and this will help you grasp useful concepts in Node JS. 

At the end of this article, you will learn the following concepts and technologies:

1. How to kick-start a Node JS application
2. Writing your first route (beginners)
3. Rendering HTML with Node JS `package.json` and explanation of what is inside
4. Implementation of Nodemailer
5. How to add Multer to your Node JS project
6. How to retrieve form data from the HTML form to our Node js Server

<hr />

### What is Node JS?

Node JS is a JavaScript framework that allows us to use JavaScript outside of the browser and on the server-side.

> 
The Server-side is the system that runs on the server (Node JS runs here), and the client-side is the software that runs on a user's web browser (Vanilla JavaScript or React JS runs here).
  
To learn more about Node JS check out the official documentation  [Here](https://nodejs.org/en/docs/) 


Let us verify that Node JS is successfully installed on your system, run the command below on your terminal.

```
node --version

```

If there is no error, the command will return the current version of the installed Node on your system.


![frame_generic_light (2).png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623272030024/Fq-6EaBvk.png)

<hr />

### What is Express JS?

Express JS is a popular framework of Node JS that makes it easier to manage web applications, Express JS is the **E** in both M**E**RN and M**E**AN stack.

To learn more about Express JS check out the official documentation  [Here](https://expressjs.com/) 

<hr />

### What is Gmail?

Gmail is a free mail service provided by Google, they enable us to send and receive information (electronic-mail) on their platform.

To learn more about Gmail and its services you can check it out  [Here](https://en.wikipedia.org/wiki/Gmail) 

<hr />

### What is Nodemailer?

Nodemailer is a Node JS module (function) that allows us to send emails from our Node JS (Server) Application.

To learn more about Nodemailer check out the official documentation  [Here](https://nodemailer.com/about/) 


<hr />


## Setting up our Node JS Application

##### 1. To begin our project create a new folder and name it `projectMailSender` 

##### 2. Right-click on the `projectMailSender` and select the `Git bash here` option

![Screenshot of the Gitbash option](https://cdn.hashnode.com/res/hashnode/image/upload/v1623268952635/DeubjsRqV.png)

Your current directory should be displayed on the bash terminal like this

![frame_generic_light (1).png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623271780524/_jKNxHlRu.png)


If you are not using Gitbash, ensure to navigate inside the `projectMailSender` folder on your `cmd`.


##### 3. Let's proceed to generate our project `package.json` file by running the code below  👇

```

yarn init -y

```

The `yarn init` command is used to create or update a `package.json` file interactively. This command will walk you through a question and answer process to create a package.


![frame_generic_light.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623271595332/i-0oVCURz.png)


We skipped some questions about the new package.json that we are creating with the `-y` flag, you can check out the questions in your next Node project by running `yarn init` without the `-y`.

> 
Note that `-y` is a shorthand for `-yes`


To learn more about the `yarn init` command check out the official documentation [Here](https://classic.yarnpkg.com/en/docs/cli/init/) 

<hr />

### What's inside package.json?

If the `yarn init` command is successful a new file named `package.json will be created in our `projectMailSender` folder.

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623358667258/u8yT15-ty.png)

Let's take a look at what is inside our `package.json` file below 👇


![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623359018912/0qCcJsTj0d.png)



##### 1. **name**: the name of our project folder.


##### 2. **version**: you can set and update your project version here

>  1 stands for a major update (version 1), second 0 stands for a minor update for the major update (version 1, with zero updates (the number of updates on version 1)), and finally the last 0 means patch, a very minor update that is not a big deal.



##### 3. **main**: every project must have an `index.js` which serves as an entry point for your project (you can change this).


##### 4. **License**: your project license goes here.

More information about your project will be stored in the `package.json` file if you do not skip the `yarn init` command with the `-y` flag.

We will be using a file name `index.js` as our project entry point, so let's proceed to create it in our project root 👇.

You can either run the command below to create the file from your terminal or create it manually.

```
touch index.js
```

Your project folder should look like this 👇

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623359863967/ogOChlPQh.png)

<hr />

## Installing Express JS 

Now that we have our project setup, let us proceed to install the Express JS into our project.
Use the command below 👇 to install `Express JS`

```

yarn add express

```

> 
Note: if you initialized your `package.json` file with `npm`, kindly stick to it to avoid conflict, let me know in the comment section if you made this mistake.


The `Express JS` package should be successfully installed by now let's take a look at our project folder structure again below 👇


![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623360414787/wRh0VvG9P.png)


You should also notice from your end that a folder named `node_modules` and a file named `yarn.lock` has been created automatically in our project folder.

### Explanation

- ### `node_modules`

This is where `yarn` or `npm` will keep the folders of our installed packages for us to make use of them later.<br  />

To learn more about `node_modules` you can check out the official documentation  [Here](https://docs.npmjs.com/cli/v7/configuring-npm/folders) 

- ### `yarn.lock` 

Because we are using `yarn` as our package manager, `yarn` auto-generate this file to keep track of our project dependencies(packages).<br />

To learn more about `yarn.lock` you can check out the official documentation  [Here](https://docs.npmjs.com/cli/v7/configuring-npm/folders) 


> 
Warning:
Do not alter the `node_modules` folder and the `yarn.lock` file manually.

<hr />

### Creating our Project Server with Express JS 

Copy and paste the code below into your project entry file (`index.js` in this tutorial)

```
// Import express into our project
const express = require("express");

// Creating an instance of express function
const app = express();

// The port we want our project to run on
const PORT = 3000;

// Express allows us to listen to the PORT and trigger a console.log() when you visit the port
app.listen(PORT, () => {
  console.log(`Server is 🏃‍♂️ on port ${PORT}`);
});
```

This is the minimum way of setting up a server with the Express Node JS framework, I have included comments that are straightforward in the code snippet 👆, so it will be present in your own project for reference.

Let's proceed to test our server by running the code below in our terminal.

```
node index.js
```

### Output 👇


![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623363322308/FeClCz8lc.png)

<hr />

## Setting up Nodemon

Now that our server is running on our terminal, let's try to change something in our `console.log()`, let's say we update the console message below 👇

```

app.listen(PORT, () => {
  console.log(`Server is currently 🏃‍♂️ on port ${PORT}`);
});


```

Save your file and check your terminal, you should notice that the previous message is still displayed, this implies that the server will not restart whenever there is a change in our project directory, we need to do this manually each time we make a change in the project by stopping the server with `ctrl + c` and starting it again with `node index.js`.

If you restart your server, you should get a new message like below 👇

![frame_generic_light.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623400668115/5XgXIc-DV.png)


To do this automatically, we can install a package called `nodemon`, `nodemon` will help us listen to changes in our project files and automatically restart the server within seconds.

Run the following command to install `nodemon` to your project

```
yarn add --dev nodemon
```

We are installing the `nodemon` package as a development dependency because it is not needed when we finally host our Node JS project to production.

To learn more about dependencies and devDependencies in `package.json` you can check it out  [Here](https://docs.npmjs.com/specifying-dependencies-and-devdependencies-in-a-package-json-file) 


Now that we've installed `nodemon` package in our project, let us set it up to do its job.

Open your `package.json` file 👇

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623401648758/dSMsW1zhs.png)

Your project `express` package should be inside the dependencies object while the `nodemon` should be inside the devDependencies, this is because we need the `express` package in our production but we only need the `nodemon` for our project development.


> 
if your `package.json` is not structured like the above, kindly check back how we installed the two packages previously.


Add the following code to your `package.json` file 👇

```
// Before the dependencies

  "scripts": {
    "start": "nodemon index.js"
  },

```
Your `package.json` file should look something like below 👇

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623452026035/9bx1_Ndp9.png)

we are basically adding a command to our project which can be accessed through `yarn` or `npm`, in this case, we are saying when we run the `yarn start` command, `yarn` should start our project with `nodemon index.js`, so that `nodemon` will start listening to our file changes.

Start the server again with the code below 👇

```

yarn start

```

You should get a response, that `nodemon` is ready for any file change and will restart the server immediately.


![frame_generic_light (1).png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623402989909/t0wi6JQsA.png)


You can proceed to change your `console.log()` message and see if the server will restart automatically.


> 
Kindly note that `nodemon` will only listen to changes to files with `.js`, `.mjs`,  and `.json` extensions, we will have to handle the refresh of the HTML and CSS files on our browser.

If you wish to handle this automatically you can click  [HERE ](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer) to install a live server on your **VScode** to listen for the HTML and CSS file changes.

Click the go-live to turn on the server for your HTML file (ensure you are currently on your HTML file)


![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623599939895/MOwuIO-3_.png)

<hr />
Now that we have our server up and running with `nodemon` listening to our file changes, let design our application interface.

1. Create a new folder and name it `public`

2. Inside the `public` folder create the following files  


- 
####  `index.html` 

- 
####  `index.css` 

- 
####  `success.html`

- 
####  `success.css`

<hr />

Copy the code below 👇 into the stated files respectively

<hr />

### index.html

```
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <!-- Css styles -->
    <link rel="stylesheet" href="./index.css" />
    <!-- Fontawesome icon library -->
    <link
      rel="stylesheet"
      href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
    />
    <title>e-Mail Sender Application</title>
  </head>
  <body>
    <form style="max-width: 500px; margin: auto">
      <h2>e-Mail Sender Application</h2>
      <div class="input-wrapper">
        <i class="fa fa-user icon"></i>

        <input
          class="input-field"
          type="text"
          placeholder="To: i.e unclebigbay@gmail.com"
          name="email"
          autocomplete="off"
          required
        />
      </div>
      <div class="input-wrapper">
        <i class="fa fa-envelope icon"></i>

        <input
          class="input-field"
          type="text"
          placeholder="Subject"
          name="subject"
          required
        />
      </div>

      <div class="input-wrapper">
        <textarea
          id="message"
          name="message"
          placeholder="Message body.."
          style="height: 200px; width: 100%"
          required
        ></textarea>
      </div>

      <div class="attachment-wrapper">
        <label for="attachment">Attachment</label>
        <input id="attachment" name="attachment" type="file" required/>
      </div>

      <button type="submit" class="send-btn">Send Mail</button>
    </form>
  </body>
</html>


```

<hr />

- ### `index.css`

```

@import url("https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap");

* {
  box-sizing: border-box;
  font-family: "Poppins";
}

h2 {
  font-size: 2rem;
}

.input-wrapper {
  display: -ms-flexbox; /* IE10 */
  display: flex;
  width: 100%;
  margin-bottom: 15px;
  font-family: "Poppins";
}

.icon {
  padding: 10px;
  background: #333333;
  color: white;
  min-width: 50px;
  text-align: center;
  font-family: "Poppins";
}

.input-field {
  width: 100%;
  padding: 10px;
  outline: none;
  font-family: "Poppins";
  border: 2px solid #333333;
}

.input-field:focus {
  box-shadow: 0 0 10px #333333;
  border: 2px solid #333333;
}

.input-wrapper textarea {
  padding: 1rem;
  border: 2px solid #333333;
}

.input-wrapper textarea:focus {
  outline: none;
  box-shadow: 0 0 10px #333333;
  border: 2px solid #333333;
}

.attachment-wrapper {
  margin: 15px 0;
}

.attachment-wrapper label {
  font-weight: 600;
}

.send-btn {
  background-color: #333333;
  color: white;
  padding: 15px 20px;
  border: none;
  cursor: pointer;
  width: 100%;
  opacity: 0.9;
  font-size: 16px;
}

.send-btn:hover {
  opacity: 1;
}

```

<hr />

- ### `success.html`

```

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    
    <link rel="stylesheet" href="./success.css" />
    <!-- Fontawesome icon library -->
    <link
      rel="stylesheet"
      href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
    />
    <title>Mail Sent</title>
  </head>
  <body>
    <i class="fa fa-paper-plane icon"></i>

    <h1>Message Delivered!!!</h1>
    <a href="./index.html">New message</a>
  </body>
</html>


```

<hr />

### `success.css`

```

@import url("https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap");

body {
  font-family: "Poppins";
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  height: 70vh;
  color: #333333;
}

a {
  color: blue;
}

a:hover {
  opacity: 0.8;
}

body .icon {
  font-size: 3rem;
}


```

<hr />

### Rendering Static Files to the Browser with Express JS

Yes, we cannot just access our HTML file directly from our browser, express must handle that for us and to this point, we can only access our application port `3000` on our terminal, let's see how we can display our HTML files on our browser using Express JS.

Update the `index.js` with the following code

```
....
// After PORT = 3000;

// Express should add our path
app.use(express.static("public"));


// Render the index.html when the user visit our project port
app.get("/", (req, res) => {
  res.sendFile("/index.html");
});

...

```

### Explanation


##### 1. `app.use(express.static("public"));`


- 
We are using the express js method called `use` to chain a middleware called `static`, which allows us to serve static files like `html`, `css`, `js`, `image`, and more.


> 
The `public` folder is defined inside the `static` middleware as our project root directory.

##### 2. `app.get` 


- 
<span id="get">`app.get()`</span> is a function that tells the server what to do when a get request at the given route is called.


##### 3. `("/", (req, res)`

- 
The forward slash signifies the root directory of our route, just like `www.hashnode.com`

- 
The `req` argument stands for **request** from that particular route, while the `res` represents **response**, which is a means to send back a response to the route caller.


##### 4. ` res.sendFile("/index.html");`


- 
Remember the `res` above 👆 us used to send back a response? the `res` has a method to send back a file to the route been called, and that is what we are doing here, we are returning the `index.html` file when the root route of the project is been hit.


### Project UI Routing on a Browser with Express JS


Routing refers to how our application's endpoints (URLs) respond to the client (browser) request through the `req` parameter, this is unique for a particular endpoint (URL).  

[www.hashnode.com](www.hashnode.com/onboard) will respond with the landing page while a request to [www.hashnode.com/onboard](www.hashnode.com/onboard) will respond with a login page 😉.

Since we have our route and static files setup, it's time to visit our project port in our browser 💃

You can either copy-paste this route 👉 **localhost:3000** to your browser address bar and hit enter or click this link [localhost:3000](http://localhost:3000/).

If all goes well, you should have this 👇 displayed on your browser.

![frame_generic_light (3).png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623438401772/ZS1obK64P.png)


You can as well visit the **success page** using this link [localhost:3000/success.html](http://localhost:3000/success.html) 


Our success page should look something like below 👇.

![frame_generic_light (4).png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623438866078/r3NoVbSbG.png)


<hr />


<div class="centered" >If you made it this far let's take a break, and celebrate 😍</div>


![fb7596eafc1adf186d0e6ed139b458d8.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1623439898942/K-fo4ZjR5.gif)

Let's drink up 👇

![giphy (4).gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1623439914103/Fp2zTwupt.gif)


<hr />


** <div class="centered"> Welcome Back 🤩</div> **

<hr />

## Handling Data from our HTML forms

Now that we have our project UI setup, let's look into handling the data submitted by the sender.

Note that, our HTML form has the following form input types

1. email - text
2. subject -  text
3. message - text
4. attachment - file

Generally, to have access to a request body through the <a href="#get">`req`</a> parameter in the `get()` function, we need a middleware package called `bodyparser`, but this is also provided by the express JS function, so we do not need to install any package for this.

Update the `index.js` with the code below

```
// After the express static middleware
...

// Body parser
app.use(express.json());
app.use(express.urlencoded({ extended: false }));

...

```


Now that we have our `body-parser` setup, let's see it in action by creating a `POST` route in our application, which we will use to `post` the entries of the sender to our server.

Add the following code below the `get` route function

```
// Post route to handle retrieving data from HTML form to server
app.post("/send_email", (req, res) => {
  console.log(req.body);
});

```
We also need to update our HTML form to have a `POST` method and also set the path to post the data to like below.

```
    <form
      action="/send_email"
      method="POST"
      style="max-width: 500px; margin: auto"
    >
```

### Explanation


##### 1. `action="/send_email"`

- The action attribute is used to specify where we want to send the form data when the form is submitted, we are sending the data to the **/send_email** route in our application.

##### 2. `method="POST"`

- The post method is good when sending sensitive information from the client (browser) to the server (backend), it hides the form values from the browser address bar unlike the `GET` method which displays it, the post method also appends form data inside the body of the HTTP request, this body is what express js is parsing and is also been retrieved using through the `req` parameter in our route functions. 

##### 3. `req.body`
- Here we are accessing the form body object from the `req` parameter which contains all the data from our HTML inputs (example ahead).

Now that we have all this setup, let us proceed to send a sample mail that will be displayed in our terminal.

Fill in the form inputs and also select an attachment then click the **send mail** button.

![frame_generic_light (6).png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623451260493/BXc1XCxm_.png)


If you have all your project set up correctly you, should have the form values displayed below in your terminal.

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623451499503/tsc87IKM_.png)


> 
That 👆 is the example of the `req.body`, this picks up the attribute name of the HTML input tags

<hr />

## The <u>POST</u> Method

![frame_generic_light (9).png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623452802484/EhPiX5yC3.png)


> Did you noticed that your browser address bar didn't change 💁‍♂️, now set your form method to `GET` and try sending a mail again, what did you notice?

<hr />


## The <u>GET</u> Method


![frame_generic_light (8).png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623452831541/SFbncJzcP.png)


The submitted data are been displayed on the address bar, imagine you are submitting your password 😱. 

> 
Which method will you use for credit cards ?.



<hr />

## Handling Files over a Node JS server

In this section, we will look into handling files coming from the client-side to our project Node JS backend application. 

In other to handle files  coming from the frontend, we need to install a package called `multer`

Multer is a node.js middleware for handling **multipart/form-data**, which is primarily used for uploading files.

Multer also has a rule, which is that **multer will not process any form which is not multipart (multipart/form-data).**

In other to make `multer` work in our project we need to add an encryption attribute to our form tag like below.

```
    <form
      action="/send_email"
      method="POST"
      enctype="multipart/form-data"
      style="max-width: 500px; margin: auto"
    >

```

Ensure to update your form tag with the new attribute, after that, we need to create a new folder called `attachments` in our project root directory

Your project structure and `index.js` should look like this


![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623454927337/3GxIb_cx5.png)

<hr />

### Configuring Multer


To configure our project to use multer, we need to first install it using the command below

```
yarn add multer

```


Once it is installed, we need to configure it in our `index.js` file.

### Setting up multer `diskStorage`

Multer allows us to specify where we want to store the attachments coming from the frontend and also define their unique names through its method called **diskStorage**.

require `multer` below where we required the `express` js

```
// Import express into our project
const express = require("express");

// Import multer
const multer = require("multer");

...
```

Copy-paste the following code, below the body-parser.

```
// Multer file storage
const Storage = multer.diskStorage({
  destination: function (req, file, callback) {
    callback(null, "./attachments");
  },
  filename: function (req, file, callback) {
    callback(null, `${file.fieldname}_${Date.now()}_${file.originalname}`);
  },
});

```

<hr />

### Explanation

#### ***`multer.diskStorage`***

#### 1.  ***`destination`***

This multer method allows us to set the destination of our attachment through a callback function, and as you can see we passed our `attachment` folder as the second argument of the callback function, this is where the attachment of a mail will be store (example coming up).

#### 2.  ***`filename`***

The second property we can set using the `diskStorage` is the file name of the incoming attachment, we do this so that every attachment will have a unique file name, we concatenate the field name (from, input name=" attachment") with underscore `_` and current date that the file is been attached in milliseconds with another underscore `_` and finally the attachment original name. 

That is super unique 😍

<hr />

Let's proceed to write middleware to actually get the attachment from the request body.

```
// Middleware to get a single attachment
const attachmentUpload = multer({
  storage: Storage,
}).single("attachment");

```
What we are doing here is that we are passing the attachment storage logic we created into the **multer** object and also telling **multer** that we are only targeting a single attachment with the `.single("attachment")`.



### Applying Multer Middleware to our Post Route

Now that we have set up a middleware with `multer`, which will help us retrieve attachments from the frontend form, let's proceed to apply it within our Post route.

Update the application post route with the following code

```
// Post route to handle retrieving data from HTML form to server
app.post("/send_email", (req, res) => {
  if (error) {
      console.log(err);
      return res.send("Error uploading file");
    } else {
      const recipient = req.body.email;
      const subject = req.body.subject;
      const message = req.body.message;
      const attachmentPath = req.file.path;
      console.log("recipient:", recipient);
      console.log("subject:", subject);
      console.log("message:", message);
      console.log("attachmentPath:", attachmentPath);
    }

});

```

### Explanation


##### 1. We invoked (call/execute) the `attachmentUpload` middleware function and extend (pass) the `req` and `res` parameter from the post route function to it. This will allow the middleware to have access to any file in the request body.


##### 2. The `attachmentUpload` also takes in a callback function which will check for any error while `attachmentUpload` processes the files from the request body.

##### 3. In the callback function, we are checking whether there is an error in the process, **`if`** any error occurs, we want to display the error in the terminal and send back a message which says `Error uploading file` as a response (`res`).

> 
A call-back function is any function that is been passed as an argument to another function, which will be executed (call-back) later in time (any point in time).

##### 4. Finally, we included an alternative statement with the **` else`** block which is executed if the previous statement is not satisfied. This implies that the `else` statement block will only run if there is no error when the middleware is trying to upload any attachments from the HTML form to the "attachment" folder. 

<hr />

### Storing Files from HTML Form to our Storage Folder

Let's proceed to test our implementation so far.

Save your script and refresh your browser (if you're not using a live server) to get updated with our node script.


Fill the form inputs and attach any file of your choice as shown below 👇 and hit the send button.

![frame_generic_light (13).png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623591706488/Jpjk8_Ha0.png)


### Output 1 👇

If all goes well, your terminal should have the following output 👇

![frame_generic_light (12).png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623591541760/9hkxSymQe.png)

You should also notice that the attachment name has changed to the naming convention logic we created earlier 👇.


![frame_generic_light (14).png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623593206815/6YLCpLHmQ.png)


### Output 2 👇

The fun part is, if you expand the *attachment* folder, you will see your attached file there 💃, thanks to **multer**, you can also check the folder directory in the file explorer.

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623591792981/jiAKUiu0U.png)


### Check your File Explorer 👇

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623595804065/xI-oIaa-8.png)


> 
You can check the complete code to this point from my GitHub gist [Here ✋](https://gist.github.com/unclebay143/afd096589fc868b86fd9edd1eb291f27) 

<hr />

## Setting up Nodemailer Package


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.

<hr />

## 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](https://cdn.hashnode.com/res/hashnode/image/upload/v1623656689509/MuEabNjvJ.png)


This is where we are going to store our **Environment-Specific** variables in a `NAME=value` format like below 👇


![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623657156443/eg1P6ECPJ.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](https://cdn.hashnode.com/res/hashnode/image/upload/v1623657621117/fV89203dW.png)


Or like this 👇

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623657755775/tng7mIc7W.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](https://www.npmjs.com/package/dotenv) 

<hr />

### 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](https://cdn.hashnode.com/res/hashnode/image/upload/v1623659537468/Gd6Od83Qn.png)

### Output 👇

![frame_generic_light (15).png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623660497030/ID5U7drLc.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) 😉.

<hr />


If you made it this far, you deserve this 👇



![standing clapping ovation](https://cdn.hashnode.com/res/hashnode/image/upload/v1623662987273/cP421AUWt.gif)


<hr />


### <div class="centered"> FINAL LAP <div>


<hr />

## 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](https://console.cloud.google.com/) **, if you have not done that before you can register  [HERE](https://console.cloud.google.com/).


If your gmail account is logged in on your browser you should have a screen display like this 👇


![google cloud platform login to dashboard](https://cdn.hashnode.com/res/hashnode/image/upload/v1623668291610/KhrcyBpBt.png)


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](https://cdn.hashnode.com/res/hashnode/image/upload/v1623701916901/ZolLeSQsD.png)


- 
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 <span class="rock">Create</span>button.

![Fill in a name for your project input box circled](https://cdn.hashnode.com/res/hashnode/image/upload/v1623669488191/JnBBhB_4W.png)



- 
After a few seconds delay your dashboard should look like this 👇


![frame_generic_light (20).png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623681938996/JjpHTBFqC.png)



- 
After loading click on **Go to APIs review** 👇


![frame_generic_light (21).png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623682116582/1BSEXeqTO.png)



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


![frame_generic_light (22).png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623682450130/GmmJO3-rgT.png)




### On the OAuth consent Screen

- 
Select external 👇 and click on the <span class="rock">create</span> button


![the OAuth consent Screen](https://cdn.hashnode.com/res/hashnode/image/upload/v1623684072803/Fwmry9wxp.png)


- 
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](https://cdn.hashnode.com/res/hashnode/image/upload/v1623682987008/nplkzkbd_.png)



- 
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](https://cdn.hashnode.com/res/hashnode/image/upload/v1623683339013/j_slmeEWQ.png)


- 
Add your email address as the test user and save 👇


![add test user section](https://cdn.hashnode.com/res/hashnode/image/upload/v1623683837342/IrTd544wv.png)



- 
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](https://cdn.hashnode.com/res/hashnode/image/upload/v1623683757586/MAR6IZiaz.png)


<hr />

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](https://cdn.hashnode.com/res/hashnode/image/upload/v1623685109518/Ne0Hcol-F.png)



-
On the new page, click on **Create Credentials** and select the **OAuth Client ID** option from the dropdown 👇


![frame_generic_light (30).png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623685435247/y7XhLC1ws.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](https://cdn.hashnode.com/res/hashnode/image/upload/v1623685708276/_y7_6_qZp.png)



- 
Choose a client name 👇

![frame_generic_light (32).png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623685886068/3ek7xYu7g.png)



- 
Scroll to the bottom of the page and add  [https://developers.google.com/oauthplayground](https://developers.google.com/oauthplayground) to the **Authorized redirect urls** section 👇

![frame_generic_light (34).png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623687329834/Qs16-lf66.png)

After adding the URL, click on the <span class="rock">Create</span> 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](https://cdn.hashnode.com/res/hashnode/image/upload/v1623686342650/ULxkLmLyO.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 🎣.

<hr />

### 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](https://cdn.hashnode.com/res/hashnode/image/upload/v1623687913604/Kz_-LddDH.png)




- 
### OAuth Playground

Remember that we set the **Authorized redirect urls** to  [https://developers.google.com/oauthplayground](https://developers.google.com/oauthplayground) ?

Now let's head to the playground to grab our refresh token 💃

click here 👉  [https://developers.google.com/oauthplayground](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 *https://mail.googl.com/* from the dropdown

![frame_generic_light (37).png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623689079977/6nfrXGZeE.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](https://cdn.hashnode.com/res/hashnode/image/upload/v1623689427949/lJP0J37pr.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](https://cdn.hashnode.com/res/hashnode/image/upload/v1623690097696/pCCJjXwED.png)



- 
Grand the application access to your Gmail account 👇

![frame_generic_light (40).png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623690200520/g0JX3eQwP.png)

And 👇

![frame_generic_light (41).png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623690320461/L8aytCc9i.png)




- 
You will be redirected back to the OAuth Playground, click on **Exchange authorization code for tokens** 👇

![frame_generic_light (42).png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623690557359/Tr6S0O9sQn.png)




- 
Copy the refresh token and keep it safe 👇


![frame_generic_light (43).png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623690779063/YCtHkM6Kn.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](https://cdn.hashnode.com/res/hashnode/image/upload/v1623698150196/aR-egfuaV.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](https://cdn.hashnode.com/res/hashnode/image/upload/v1623697073219/237oAgSvg.png)


#### 2. Remember the refresh access token 👇 also?

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623698422273/0aGBNAeRE.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](https://cdn.hashnode.com/res/hashnode/image/upload/v1623698595295/89j-zdurX.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)


<hr />

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**](https://gist.github.com/unclebay143/643675b5d9112f7b16dac7af60e81af9) 

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](https://cdn.hashnode.com/res/hashnode/image/upload/v1623700700290/RI11jhu1a.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](https://cdn.hashnode.com/res/hashnode/image/upload/v1623702207642/WI8plCd78.png)


- 
Proceed to check your recipient mailbox, your sent e-mail should be received 💃.

![frame_generic_light (51).png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623702169184/h32XS0PZs.png)



- Full mail body with attachment displayed 👇

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1623702355866/CQ8mGUQQnO.png)


<hr />

### 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](https://nodejs.org/api/fs.html) 


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");
      }
    });
  }
});

```

<hr />

You can get the completed Github Gist for the `index.js` file [Here](https://gist.github.com/unclebay143/d21748097ab04cbad551508f79d538d4) 

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](https://cdn.hashnode.com/res/hashnode/image/upload/v1623766288820/jpCRyVgkZ.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 stuff.

### Useful Links 👇


- Final Project Link: 

- Hosted Version Link: 


<hr />


![giphy (5).gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1623766314820/wfaXUoHmq.gif)

<hr />

Wow, what a journey, I am glad you made it to the end of this article, 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.

Let's connect on

-  [Twitter @unclebigbay143](https://twitter.com/intent/follow?screen_name=unclebigbay143) 
-  [LinkedIn @unclebigbay](https://www.linkedin.com/in/unclebigbay/) 

<hr />

%%[reaction-id]

<hr />

See you in the next article. Bye Bye 🙋‍♂️

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1622627809083/3i7ThmW-M.png)


If you found this helpful and want to support my blog, you can also [buy me a coffee](www.buymeacoffee.com/unclebigbay).