How to implement email functionality with Node.js, React.js, Nodemailer, and OAuth2
Tags: react, node, javascript, nodemailer
Published: 2021-03-30 20:06:28

#### Nodemailer is a Node.js module that allows users to send messages directly to your email. This article is a guide to help you connect your Node.js server to your GMail account by using OAuth2 in addition to creating a React.js Form to send an email. 

##### Steps:
- Set up the Node.js Server
- Set up Nodemailer part 1
- Configure OAuth2
- Set up Nodemailer part 2
- Set up Nodemailer part 3
- Set up React.js
- Set up Nodemailer part 4 
- Finish React

To successfully go through this article, you need to have node and npm installed on your device, a code editor, as well as a basic understanding of Node.js and React.js. This guide does go back and forth between the backend, OAuth2, and frontend, so bare with me! 

To check for node and npm versions on your system, run the following commands to retrieve the version numbers: 
```
node -v
v15.9.0
npm -v
7.5.3
```
### Setting up the Node.js server
If you have node and npm installed, let’s get started.

Create a directory for your project. For this we will be using nodemailerAPI.
`mkdir nodemailerAPI`


Go into your new `nodemailerAPI` directory and create a `server.js` file, which will serve as our main file.
```
cd nodemailerAPI
touch server.js
```


In the terminal, run the following command to initialize a package.json file:
```
npm init -y
```

Now let’s install the packages that we will need.
For this project, we will be using `Express.js, Nodemailer, and Dotenv`. 
```
npm i express nodemailer dotenv
```
*Time for some node.js coding!*

We are going to require our packages and run our server using `Express.js`. Open your directory in your code editor and input the following code:
```javascript
const express = require("express");
const nodemailer = require("nodemailer");
const app = express();
require("dotenv").config();
 
const port = 3001;
app.listen(port, () => {
 console.log(`Server is running on port: ${port}`);
});
```
Once the above code is inputted, you will be able to run your server with the following command and see the console.log:
```
node server.js
Server is running on port: 3001
```
 
Our server is now running, which is great news! Let’s stop the server for now with `control-c` and move on to our next step, setting up NodeMailer.

### Setting up NodeMailer

Nodemailer requires 3 things to begin running:
1. A transporter object
2. A mailOptions Object
3. A sendMail method

#### Step 1: Lets setup the transporter object:
```javascript
let transporter = nodemailer.createTransport({
 service: "gmail",
 auth: {
   type: "OAuth2",
   user: process.env.EMAIL,
   pass: process.env.WORD,
   clientId: process.env.OAUTH_CLIENTID,
   clientSecret: process.env.OAUTH_CLIENT_SECRET,
   refreshToken: process.env.OAUTH_REFRESH_TOKEN,
 },
});
```
*Important Note: The user and pass keys are your own email and your email password. We will be getting the clientId, clientSecret, and refreshToken as soon as we set up OAuth2.*

As you can see, storing these variables in a `.env` file are incredibly important to your privacy, so lets create a `.env` file to store our auth values:
```
touch .env
```

Within the `.env` file, input the following:
```
EMAIL=youremail@gmail.com
WORD=youremailpassword
OAUTH_CLIENTID=
OAUTH_CLIENT_SECRET=
OAUTH_REFRESH_TOKEN=
```

Before moving on to step two in the NodeMailer process, let's set up OAuth2 and get those other values!

### Configuring OAuth2

Click the following link to go to your [Google Cloud Platform](https://console.cloud.google.com/home) dashboard. Near the top left, you will see a dropdown arrow. Once you click it, a project info modal will come up. 

![](https://i.imgur.com/lJuW6s7.png)

On the popup modal, select the new project option. 
![](https://i.imgur.com/GvWGLNp.png)

After clicking the new project button, you will see a new screen with a form to name your new project. For this project, we will be using `nodemailerAPI` and you can skip the location input box. Click create.

![](https://i.imgur.com/JyGJaQ0.png)

After you click create, it will generate your new project and you will be redirected back to your dashboard.

On the same drop down(top left) as where you went to create the new project, you will be able to see your new project, which you can now select. 

Once selected, open the top-left nav menu and select the following:

![](https://i.imgur.com/Z42dhkh.png)

Once you click the OAuth consent screen, you will be brought to the following page, where you will click external:
![](https://i.imgur.com/LO59lRR.png)

After clicking create, you will be taken to a new page where you are required to fill in information regarding your newly created project: 
![](https://i.imgur.com/iXzJpJP.png)

Once you have filled in the above information and click save & continue, you will see the Scopes phase of the OAuth configuration. *This page can be skipped so click save and continue here.* 

The next page is where you will add yourself as the test user:
![](https://i.imgur.com/jLvADnZ.png)

Add yourself as a test user using your gmail and then click save and continue. The next page will be a summary of all of the inputted information, which is the end of that phase. 

Next, click credentials on the left hand side, then click create credentials and select OAuth client ID:
![](https://i.imgur.com/njlSsI7.png)

You will be redirected to the following page, where you create an OAuth clientID:
Change the application type to Web Application. We will be using OAuth2 Playground [https://developers.google.com/oauthplayground](https://developers.google.com/oauthplayground) as the Authorized redirect URI:
![](https://i.imgur.com/BHUlngN.png)

Once those fields are inputted, you can click create. 

Next, you will be presented with your OAuth clientID and Client Secret:
![](https://i.imgur.com/RpAwtFV.png)

Copy both of those values and return to your `.env` file to fill in those empty keys.

Now you should only be missing one key-value: `OAUTH_REFRESH_TOKEN`, so let's go get it. 

Head to the [OAuth Playground](https://developers.google.com/oauthplayground/). Once there, click the gear on the upper right hand side of the screen to bring up the configuration. Click `Use your own OAuth credentials` and input the Client ID and Secret that you just inputted into your `.env` file. 
![](https://i.imgur.com/iQegJRB.png)


On the left hand side of the screen, you will see a large list of API’s. Select Gmail API v1 and the first option under the dropdown:
![](https://i.imgur.com/2dynAgj.png)

Click authorize API’s on the bottom right. This will bring you to a popup to select your gmail account. Choose your account and select continue on the next page.
![](https://i.imgur.com/nlyD3AZ.png)

Then, a new popup will come up and ask for you to grant your app permission to send email from your gmail. Click allow here.

After you click allow, you will be redirected back to the OAuth playground dashboard. Click `exchange authorization code for tokens` to receive your refresh token for your `.env` file:
![](https://i.imgur.com/6ayzGud.png)

Our OAuth2 configuration is done, so lets head back to node. 

### NodeMailer Step 2:
By now, you should have all of your key-value pairs filled out within your `.env` file. Let's verify the transporter underneath the created transporter from Nodemailer step 1.
```javascript
transporter.verify((err, success) => {
 err
   ? console.log(err)
   : console.log(`=== Server is ready to take messages: ${success} ===`);
});
```

Within the terminal, run the server again and check for the console.log:
```
node server.js 
Server is running on port: 3001
=== Server is ready to take messages: true ===
```

We get true! *Very exciting*

Underneath the verification, lets create a test `mailOptions` object:
```javascript
let mailOptions = {
 from: "test@gmail.com",
 to: process.env.EMAIL,
 subject: "Nodemailer API",
 text: "Hi from your nodemailer API",
};
```

### Nodemailer Step 3:

Next, lets send the `mailOptions` through a `transporter sendMail` method:
```javascript
transporter.sendMail(mailOptions, function (err, data) {
 if (err) {
   console.log("Error " + err);
 } else {
   console.log("Email sent successfully");
 }
});
```

Now, lets run the server again and within the terminal, you will see:
```
node server.js 
Server is running on port: 3001
=== Server is ready to take messages: true ===
Email sent successfully
```

Check your email as it will be there!

*Checkout the full server.js code at this point:*

```javascript
const express = require("express");
const nodemailer = require("nodemailer");
const app = express();
require("dotenv").config();
 
let transporter = nodemailer.createTransport({
 service: "gmail",
 auth: {
   type: "OAuth2",
   user: process.env.EMAIL,
   pass: process.env.WORD,
   clientId: process.env.OAUTH_CLIENTID,
   clientSecret: process.env.OAUTH_CLIENT_SECRET,
   refreshToken: process.env.OAUTH_REFRESH_TOKEN,
 },
});
 
transporter.verify((err, success) => {
 err
   ? console.log(err)
   : console.log(`=== Server is ready to take messages: ${success} ===`);
});
 
let mailOptions = {
 from: "test@gmail.com",
 to: process.env.EMAIL,
 subject: "Nodemailer API",
 text: "Hi from your nodemailer API",
};
 
transporter.sendMail(mailOptions, function (err, data) {
 if (err) {
   console.log("Error " + err);
 } else {
   console.log("Email sent successfully");
 }
});
 
const port = 3001;
app.listen(port, () => {
 console.log(`Server is running on port: ${port}`);
});
```

As of now, there is no way to access nodemailer from the frontend as we do not have a route established. So let’s create that route. 

Our `transporter.sendMail` is already set up so this will be fast! All we are doing is taking the `transporter.sendMail` and placing it inside of a function that is attached to a route. We have also attached a response action to send a status back, which will help determine the success in a later cURL test.
```javascript
app.post("/send", function (req, res) {
 let mailOptions = {
   from: "test@gmail.com",
   to: process.env.EMAIL,
   subject: "Nodemailer API",
   text: "Hi from your nodemailer API",
 };
 
 transporter.sendMail(mailOptions, function (err, data) {
   if (err) {
     console.log("Error " + err);
   } else {
     console.log("Email sent successfully");
     res.json({ status: "Email sent" });
   }
 });
});
```

Let’s test this new route using cURL. 

With your `nodemailerAPI` server running, open up your terminal, and run the following command in a new terminal tab:
```
curl -d -url http://localhost:3001/send
```

After running the cURL  command, you will see our response come back in the terminal:
```
{"status":"Email sent"}
```

You should see an email in your inbox; Our route is now ready to use on the front-end(kindof)!

======================================================
## Setup React.js

#### React Setup Step 1:
Lets head back to your terminal to create the new react project, which we will call `nodemailer-form`  by running the following command:
```
npx create-react-app nodemailer-form
```

Open this new react directory in your code editor and within `src/App.js`, you will see the default react code that comes with create-react-app. Let’s remove all of it except for the top level `div`:

![](https://i.imgur.com/pZpwsd5.png)

For the purpose of this guide, we will only be coding within `App.js`, which is now blank and ready for us to get to work. 

#### Import & setup the useState Hook

At the top line of `App.js`, import useState like so:
```
import { useState } from "react";
```

Our `nodemailerAPI` will be looking for a name, email, and message when sending an email. So, let’s set up our `useState` to reflect that:
```javascript
const [mailerState, setMailerState] = useState({
   name: "",
   email: "",
   message: "",
 });
```

Now let's set up a function to handle the change when we type into our future input boxes. 

```javascript
function handleStateChange(e) {
   setMailerState((prevState) => ({
     ...prevState,
     [e.target.name]: e.target.value,
   }));
 }
```

Next, let’s create a form and attach the `handleStateChange` to each input box as well as the name and value. 
```javascript
return (
   <div className="App">
     <form>
       <fieldset>
         <legend>React NodeMailer Contact Form</legend>
         <input
           placeholder="Name"
           onChange={handleStateChange}
           name="name"
           value={mailerState.name}
         />
         <input
           placeholder="Email"
           onChange={handleStateChange}
           name="email"
           value={mailerState.email}
         />
         <textarea
           placeholder="Message"
           onChange={handleStateChange}
           name="message"
           value={mailerState.message}
         />
  <button>Send Message</button>
       </fieldset>
     </form>
   </div>
 );
```

If you run your react project with `npm start` and view your project on `localhost:3000`, it is going to be one of the ugliest forms you’ve ever seen. Let’s do a quick fix and add some inline styles to make it sort of look like a contact form:
```javascript
<div className="App">
     <form
       style={{
         display: "flex",
         height: "100vh",
         justifyContent: "center",
         alignItems: "center",
       }}
     >
       <fieldset
         style={{
           display: "flex",
           flexDirection: "column",
           justifyContent: "center",
           width: "50%",
         }}
       >
         <legend>React NodeMailer Contact Form</legend>
         <input
           placeholder="Name"
           onChange={handleStateChange}
           name="name"
           value={mailerState.name}
         />
         <input
           placeholder="Email"
           onChange={handleStateChange}
           name="email"
           value={mailerState.email}
         />
         <textarea
           style={{ minHeight: "200px" }}
           placeholder="Message"
           onChange={handleStateChange}
           name="message"
           value={mailerState.message}
         />
         <button>Send Message</button>
       </fieldset>
     </form>
   </div>
```

At this point, your form should look like this:
![](https://i.imgur.com/PKTglML.png)

It is still not going to win any style contest(maybe in the 90’s), but at least it looks the part!

It doesn't work yet so we need to get it to act the part too!
We need to create a function that actually posts to the `nodemailerAPI` route and attach it to the form:
```javascript
const submitEmail = async (e) => {
   e.preventDefault();
   console.log({ mailerState });
   const response = await fetch("http://localhost:3001/send", {
     method: "POST",
     headers: {
       "Content-type": "application/json",
     },
     body: JSON.stringify({ mailerState }),
   })
     .then((res) => res.json())
     .then(() => {
       setMailerState({
         email: "",
         name: "",
         message: "",
       });
     });
 };
```

```javascript
<form
   style={{
     display: "flex",
     height: "100vh",
     justifyContent: "center",
     alignItems: "center",
   }}
    onSubmit={submitEmail}
 >
```

Now, our form should be good to go, or is it? If you have tried to submit the form, nothing is going to happen. If you are using Google Chrome and open up your console, you will see the following: 
![](https://i.imgur.com/zEUlpVY.png)

Our `mailerState` is logging as expected but we are getting a `CORs error`. 

Remember earlier, I mentioned that our backend API route is *kind-of* ready? We have come to a point where we need to return to our API and fix a couple things.

### Nodemailer part 4

Let’s go back to our node server and install our last dependency: `CORs`. Within the nodemailer API directory, run the following command: 
```
npm i cors
```

Now, `require cors` at the top of your `server.js` file and tell the app to use cors as middleware. The top of your `server.js file` should look like this: 
```
const express = require("express");
const nodemailer = require("nodemailer");
const app = express();
const cors = require("cors");
require("dotenv").config();
 
// middleware
app.use(express.json());
app.use(cors());
```

While we are on the backend, we need to change the `mail options` to reflect the request from our React side. 

Our current mail options looks like this: 
```javascript
let mailOptions = {
   from: "test@gmail.com",
   to: process.env.EMAIL,
   subject: "Nodemailer API",
   text: "Hi from your nodemailer API",
 };
```

But this does us no good as this is what is sent every time this route is hit, so we need to change it to look like this: 
```javascript
let mailOptions = {
   from: `${req.body.mailerState.email}`,
   to: process.env.EMAIL,
   subject: `Message from: ${req.body.mailerState.email}`,
   text: `${req.body.mailerState.message}`,
 };
```

Lets also update the `transporter.sendMail` to send json back to react so we can alert the user of the email being sent: 
```javascript
transporter.sendMail(mailOptions, function (err, data) {
   if (err) {
     res.json({
       status: "fail",
     });
   } else {
     console.log("== Message Sent ==");
     res.json({
       status: "success",
     });
   }
 });
```

So, our final `server.js` file should look like this:
```javascript
const express = require("express");
const nodemailer = require("nodemailer");
const app = express();
const cors = require("cors");
require("dotenv").config();
 
// middleware
app.use(express.json());
app.use(cors());
 
let transporter = nodemailer.createTransport({
 service: "gmail",
 auth: {
   type: "OAuth2",
   user: process.env.EMAIL,
   pass: process.env.WORD,
   clientId: process.env.OAUTH_CLIENTID,
   clientSecret: process.env.OAUTH_CLIENT_SECRET,
   refreshToken: process.env.OAUTH_REFRESH_TOKEN,
 },
});
transporter.verify((err, success) => {
 err
   ? console.log(err)
   : console.log(`=== Server is ready to take messages: ${success} ===`);
});
 
app.post("/send", function (req, res) {
 let mailOptions = {
   from: `${req.body.mailerState.email}`,
   to: process.env.EMAIL,
   subject: `Message from: ${req.body.mailerState.email}`,
   text: `${req.body.mailerState.message}`,
 };
 
 transporter.sendMail(mailOptions, function (err, data) {
   if (err) {
     res.json({
       status: "fail",
     });
   } else {
     console.log("== Message Sent ==");
     res.json({
       status: "success",
     });
   }
 });
});
 
const port = 3001;
app.listen(port, () => {
 console.log(`Server is running on port: ${port}`);
});
```

Our `nodemailerAPI` part of the guide is complete. Lets tackle the last little bit of our `nodemailer-form` in React.

### Back to our form in React
We just need to update the `submitEmail` function to wait for the response. It is waiting for the backend to tell it if the status is a fail or if the status is a success and will give an alert accordingly. 
```javascript
const submitEmail = async (e) => {
   e.preventDefault();
   console.log({ mailerState });
   const response = await fetch("http://localhost:3001/send", {
     method: "POST",
     headers: {
       "Content-type": "application/json",
     },
     body: JSON.stringify({ mailerState }),
   })
     .then((res) => res.json())
     .then(async (res) => {
       const resData = await res;
       console.log(resData);
       if (resData.status === "success") {
         alert("Message Sent");
       } else if (resData.status === "fail") {
         alert("Message failed to send");
       }
     })
     .then(() => {
       setMailerState({
         email: "",
         name: "",
         message: "",
       });
     });
 };
```

So when you try to send an email, you will get the following alert and you will see the status sent from the backend in the console: 
![](https://i.imgur.com/Xb0em5R.png)

By this point, you have successfully implemented Nodemailer with OAuth2, React.js, Node.js, and Express.js. Now you can enjoy adding this to your future projects!