DEV Community

Mateus Ferreira
Mateus Ferreira

Posted on

How to build an email notification API in less than 30 minutes with Node.js and Docker

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:

mkdir email-notification-api
Enter fullscreen mode Exit fullscreen mode
cd email-notification-api
Enter fullscreen mode Exit fullscreen mode
npm init -y 
Enter fullscreen mode Exit fullscreen mode

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.

touch .gitignore
Enter fullscreen mode Exit fullscreen mode

And in your .gitignore file:

node_modules
.env
Enter fullscreen mode Exit fullscreen mode

We are gonna talk about the .env file in a minute, but first, lets start our git repository:

git init
Enter fullscreen mode Exit fullscreen mode
git add .
Enter fullscreen mode Exit fullscreen mode
git commit -m "first commit"
Enter fullscreen mode Exit fullscreen mode

Now, lets install some dependencies:

npm install express nodemailer dotenv cors
Enter fullscreen mode Exit fullscreen mode

And as a dev dependencies, we're gonna install nodemon:

npm install --save-dev nodemon
Enter fullscreen mode Exit fullscreen mode

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:

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'))
Enter fullscreen mode Exit fullscreen mode

For specific express.js syntax, refer to the documentation.
Now, we need to create a script to run the app. In the root folder, edit your package.json file, adding the following script:

{
  ...
  "scripts": {
    "start": "nodemon src/app.js"
  }  
}

Enter fullscreen mode Exit fullscreen mode

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:

EMAIL_ADDRESS=youremail@email.com
EMAIL_PASSWORD=youremailpassword
RECIPIENT_ADDRESS=therecipientemail@email.com
CLIENT_ORIGIN=http://yourclientorigin.com
Enter fullscreen mode Exit fullscreen mode

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:

//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
}
Enter fullscreen mode Exit fullscreen mode

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:

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'))
Enter fullscreen mode Exit fullscreen mode

To test the email notification endpoint using curl:

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
Enter fullscreen mode Exit fullscreen mode

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 to understand more about this process.

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

node_modules
.env
Enter fullscreen mode Exit fullscreen mode

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:

# 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"]
Enter fullscreen mode Exit fullscreen mode

Then we build the docker image running:

docker build -t email-notification-api:v1.0.0 .
Enter fullscreen mode Exit fullscreen mode

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:

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
Enter fullscreen mode Exit fullscreen mode

Now you can test it again. That's it! You can find the final code in this repo.

Top comments (0)