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
cd email-notification-api
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.
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:
git init
git add .
git commit -m "first commit"
Now, lets install some dependencies:
npm install express nodemailer dotenv cors
And as a dev dependencies, we're gonna install nodemon:
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
:
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.
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"
}
}
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
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
}
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'))
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
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
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"]
Then we build the docker image running:
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:
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.
Top comments (0)