How to build an email notification API in less than 30 minutes with Node.js and Docker
Published: 2022-02-01 19:58:34

In this article, I'm gonna go through the process of building an email notification API for when you have contact forms in our front-end and want to receive the messages submitted from it in your email inbox. This could also be the base for more complex email services. 

For that, I'm gonna be using Node.js with Express and Docker for shipping the API to production. So, first of all, create a directory and start a brand new Node.js project: 
```shell
mkdir email-notification-api
```
```shell
cd email-notification-api
```
```shell
npm init -y 
```
Before starting a git repository in the new folder, you need to add a .gitignore file, so that large files such as node_modules can be ignore, as well as keeping sensitive information such as credentials away from version control. 

```shell
touch .gitignore
```
And in your .gitignore file: 

```
node_modules
.env
```
We are gonna talk about the .env file in a minute, but first, lets start our git repository:
```shell
git init
```
```shell
git add .
```
```shell
git commit -m "first commit"
```
Now, lets install some dependencies: 
```shell
npm install express nodemailer dotenv cors
```
And as a dev dependencies, we're gonna install [nodemon](https://www.npmjs.com/package/nodemon):
```shell
npm install --save-dev nodemon
```

After that we need to create an src folder that is going to wrap our app and create an entry point for the application, which in my case is `src/app.js`:
```javascript
const express = require('express')

//initializes our express app
const app = express()

//initializes our http routes
const routes = express.Router()

//tell our app to use this routes
app.use(routes)

//our testing route
routes.get('/', (req, res) => {
    res.send('hello world')	
})

//exposes our app to port 5000 on localhost
app.listen(5000, () => console.log('server running on port 5000'))
```
For specific express.js syntax, refer to the [documentation](https://expressjs.com/).
Now, we need to create a script to run the app. In the root folder, edit your package.json file, adding the following script:
```json
{
  ...
  "scripts": {
    "start": "nodemon src/app.js"
  }  
}

```
Run `npm run start` and wait for the server to be up. You can test the root endpoint we've created by hiting http://localhost:5000 in your browser or through a http client such as Postman, Insomnia or Curl. 

Now we need to actually create the email service, and for that we need to create a .env file for our [environment variables](https://nodejs.dev/learn/how-to-read-environment-variables-from-nodejs):
```
EMAIL_ADDRESS=youremail@email.com
EMAIL_PASSWORD=youremailpassword
RECIPIENT_ADDRESS=therecipientemail@email.com
CLIENT_ORIGIN=http://yourclientorigin.com
``` 
Explaning the variables we've added: `EMAIL_ADDRESS` and `EMAIL_PASSWORD` refer to the email that is going to actually send the messages to the recipient. It could be something like `contact@mywebsite.com` as long as you've previously created it. the `RECIPIENT_ADDRESS` is the email the messages will be send to. It could be your personal email or the apps administrator email. Finally,`CLIENT_ORIGIN` refers to the address of the client-side application where the contact forms are in. We need that so we can set a cors policy and only accept requests from this application. 

The next step is to create a config folder as subdirectory of our src folder and touch a mailer.config.js file in there. So our config file path will be `src/config/mailer.config.js`. And this file will have the following content:
```javascript
//the lib we're using to send emails 
const nodemailer = require('nodemailer')
//the lib we are using to read 
//the environment variables from the .env file
require('dotenv').config()

//I'm using a gmail address to send the emails,
//refer to nodemailer documentation for further configurations 
const transport = nodemailer.createTransport({
	service: 'Gmail',
	auth: {
		user: process.env.EMAIL_ADDRESS,
		pass: process.env.EMAIL_PASSWORD,
	},
})

module.exports = {
  transport
}
```
Now we need to create the endpoint for sending the emails. You can create a `routes.js` file for that but for the sake of this tutorial I'll just add it to my app.js file. So our new app.js will be like: 
```javascript
const express = require('express')
//we import our configured transport object
const {transport} = require('./config/mailer.config')
//this will set cors policy
const cors = require('cors')
require('dotenv').config()

const app = express()

const routes = express.Router()

const corsOptions = {
	origin: process.env.CLIENT_ORIGIN
}

app.use(cors(corsOptions))
//this will parse json bodies from requests
app.use(express.json())
app.use(routes)

//this is the default post route for users' form submit
routes.post('/mail', async (req, res) => {
  try {
    //these are attributtes inputted by the user
    const {name, email, subject, message} = req.body 

    await transport.sendMail({
      to: process.env.RECIPIENT_ADDRESS,
      subject: `[My App] ${subject}`,
      html: `
      <h1>New Message from: ${name}</h1>
      <h3>Contact email: ${email}</h3>
      <p>${message}</p>
      `
    })
    //status 200 means our request was successful
    res.status(200).json({message: "Successfully sent message"})
  } catch (e) {
    res.status(400).json(e.message)  
  }
})

app.listen(5000, () => console.log('server running on port 5000'))
```
To test the email notification endpoint using curl: 
```shell
curl -d '{"name": "Mateus", "email": "myemail@email.com", "subject": "Test", "message": "my test message"}' -H "Content-Type: application/json" -X POST http://localhost:5000/mail
```
You should receive a success message and opening the recipient inbox you'll see the email. Sometimes when using Gmail, it will block our node app from sending the message and request some additional configuration. In that case, you'll receive a link in the response with the instructions to work around that problem. 

## Containerizing the app with Docker 

Now, if you want to ship this to production, Docker is a great tool for containerizing your app. As always, refer to [Docker's documentation](docs.docker.com/) to understand more about this process. 

The first thing you need to do, is to create a .dockerignore file: 

```
node_modules
.env
```

We want to create the node modules when building the docker image, and environment variables can change depending on the environment the app is running (kinda obvious). So we want to pass that as arguments when running the container. 

The next step is to create a Dockerfile, as the following: 

```Dockerfile
# the node version that will run the app inside the container
FROM node:alpine
# the app's directory inside the container
WORKDIR /app
# here we copy our package.json to the workdir
COPY ["package.json", "package-lock.json*", "./"]
# then it will install the dependencies and create node_modules
RUN npm install --production
# here we copy the rest of the files to the workdir 
COPY . .
# here we specify what command docker should run when starting the container
CMD ["node", "src/app.js"]
```
Then we build the docker image running: 
```shell
docker build -t email-notification-api:v1.0.0 .
```
If you wish so, run `docker images` to see the list of docker images you have. 

Now we need to create the container to run the image we've created, exposing port 5000 inside the container to port 5000 in localhost. Also, we are going to specify our environment variables:
```shell
docker container run -d -p 5000:5000 -e EMAIL_ADDRESS=senderaddress@email.com -e EMAIL_PASSWORD=senderemailpw -e  RECIPIENT_ADDRESS=recipientaddress@email.com -e CLIENT_ORIGIN=http://myclientapp.com --name email-notification-api email-notification-api:v1.0.0
```
Now you can test it again. That's it! You can find the final code in [this repo](https://github.com/mateusasferreira/email-notification-api).