In this tutorial, I will show you how to make your very own email bot using Node, Express, Nodemailer and node-cron.
You will learn how to program the bot to send emails throughout the day to friends and family. You will also learn how to send messages for specific dates like a friend's birthday.
Prerequisites
It would help to have basic working knowledge of JavaScript.
Checking to make sure Node is installed
Before we can get started writing code, we need to first check and see if Node is installed on the computer.
If you are using a Mac, then open up the Terminal app.
If you are using Windows, then open up the Command Prompt.
In the command line, run node -v
. If installed, it will come back with a version number like v16.10.0
.
If it is not installed, then you can install Node from the website.
Creating the project folder
First, go to the location where you want to add the folder. I am going to choose the Desktop as my location.
In the command line, use the cd
command to change directory.
cd Desktop
Then use the mkdir
command to create a new folder in that directory. I am going to name our project email-bot
.
mkdir email-bot
You should see the new folder show up in the computer.
We will use the cd
command again to change directories into the email-bot
folder.
cd email-bot
In the command line, you should see that we have successfully changed to the email-bot
folder.
Creating the package.json file
A package.json
file contains a lot of important information for our project including name, version, scripts and dependencies.
Run the command npm init --yes
or npm init --y
. That will create a default package.json
file that you can modify later.
This is what the default package.json
file will look like.
Creating the server.js file
This file will contain the bulk of our logic for sending emails.
In the command line, use the touch
command to add a server.js
file.
touch server.js
Creating an .env file
The .env
file will contain all of the information for the email addresses.
This is a special type of file that contains sensitive information that you don't want getting into the wrong hands.
That is why it is important to never commit your .env
file to GitHub.
In the command line, run touch .env
.
touch .env
Creating a .gitignore file
In this file, you will list out the names of files and folder you want Git to ignore when pushing your changes to GitHub.
Later on, we will be adding the .env
file to our .gitignore
.
To add a .gitignore
file, run the command touch .gitignore
.
touch .gitignore
Installing Express, Nodemailer, dotenv and node-cron
Express is a framework that is used with Node to help create server-side apps.
The dotenv
package, is used to load our environment variables into the server.js
file. Our environment variables will be the email addresses from the .env
file.
Nodemailer will be used to send the emails.
The node-cron package, will be used to schedule the dates and times for the email delivery.
In the command line, we can install all of these packages at once.
Run this command in the command line.
npm i express nodemailer node-cron dotenv
Now it is time to move to the code editor and start coding. I will be using Visual Studio Code, but you are free to use another code editor.
Open up the project in your editor and check to make sure you have all of the files we created.
The package-lock.json
file and node_modules
folder were created when we installed the packages.
Adding to the .gitignore file
You don't want to push the node_modules
folder to GitHub because it is a really large folder with many files and subfolders.
You also don't want to push your .env
file to GitHub because it contains very sensitive information that you want to keep hidden.
Open up the .gitignore
file and add the node_modules
folder and .env
file.
I am also going to add the .DS_Store
to the .gitignore
file. This .DS_Store
file was created when we created the email-bot
folder.
This is what your .gitignore
file should look like.
.env
node_modules/
**/.DS_Store
Adding the environment variables to the .env file
The first variable we are going to add is for the port number. The number we will use is 3000.
When naming environment variables, it is common practice to use all caps and underscores to separate words.
PORT = 3000
The next variable will be for our personal email address.
PERSONAL_EMAIL = your personal email address goes here
Then we will add the password for our personal email address.
EMAIL_PASSWORD = your personal email password goes here
The last variable will be the email address we want to send messages to.
FRIEND_EMAIL = friend's email address will go here
This is what your .env
file should look like.
PORT = 3000
PERSONAL_EMAIL = your personal email address goes here
EMAIL_PASSWORD = your personal email password goes here
FRIEND_EMAIL = friend's email address will go here
Adding the dotenv module to the server.js
If you want to load modules into your file, then you will need to use the require()
function.
This is the code to load the dotenv module into the server.js file and configure it.
require('dotenv').config();
Loading the environment variables into the server.js
We now need to load all of the variables from the .env
file into the server.js
file.
To load the variables, we have to use process.env
followed by the name of the variable.
This is what the code looks like for the PORT
variable.
process.env.PORT
This is what the code will look like when we add all of the environment variables to the server.js
file
require('dotenv').config();
const port = process.env.PORT || 3000;
const personalEmail = process.env.PERSONAL_EMAIL;
const emailPassword = process.env.EMAIL_PASSWORD;
const friendEmail = process.env.FRIEND_EMAIL;
For the port variable, it is common to add a logical OR (||) operator followed by the number for the port.
The reason why we do this is because if our process.env.PORT
doesn't work, then we tell the computer to use 3000.
Creating an Express server
We first have to add express to our server.js file.
const express = require('express');
Then we create a new express application.
const app = express();
Then we use the listen()
method which listens for connections on a given port.
The listen()
method will take in a port number and a callback function.
Our callback function will return a console.log
which displays the message "The server has started at http://localhost:3000".
app.listen(port, () => {
console.log(`The server has started at http://localhost:${port}`)
});
Starting the server
Before we start the server, we will be adding one more package called nodemon.
The nodemon package detects changes made to the file and will automatically restart the server for us.
In the command line for the project folder, run npm i nodemon
.
npm i nodemon
In the package.json
file under the scripts
, change the "tests"
to "start"
. Then change the "echo \"Error: no test specified\" && exit 1"
to "nodemon server.js"
.
"scripts": {
"start": "nodemon server.js"
},
Go back to the command line, and run npm start
.
You should see this result.
To stop the server, press Ctrl-C
on your keyboard.
Creating the email messages
For our bot, we will create two different messages. One good morning message and one Happy Birthday message.
Inside the server.js
file, create a variable called morningMsg
and assign the string "Good morning! Hope you have a beautiful day!"
const morningMsg = "Good morning! Hope you have a beautiful day!";
We will then create a birthdayMsg
and assign the string "Happy Birthday! You rock!!!!"
const birthdayMsg = "Happy Birthday! You rock!!!!";
Creating the message objects
We will create two message objects which contain the information for the email sender, receiver, subject line and message.
This is what the code looks like for the good morning message.
let sendMorningMsg = {
from: personalEmail,
to: personalEmail,
subject: "It's a beautiful morning",
text: morningMsg
};
This is what the code looks like for the birthday message.
let sendBirthdayMsg = {
from: personalEmail,
to: personalEmail,
subject: "Hooray it's your Birthday",
text: birthdayMsg
};
For now, the messages will be sent to our personal email address since we are still just testing everything.
When we are finished, we can change the to
field to have the friend's email address.
Creating the transporter
The transporter in nodemailer is responsible for sending our messages from our email account.
Please note:
If you are using Gmail, there are extra steps required for setup because of the authentication and security with Google.
To setup your Gmail account with Nodemailer, please read through this detailed tutorial.
If you are not using Gmail, then follow along with these steps.
The first step is to add Nodemailer to the server.js file.
You can add it with the rest of the imports at the top of the page.
const nodeMailer = require('nodemailer');
We will then use the createTransport()
method to add all of our information for the email provider.
let transporter = nodeMailer.createTransport({
service: 'outlook',
port: 587,
secure: false,
auth: {
user: personalEmail,
pass: emailPassword
}
});
Verify the email address
We can add a condition to test if there was an error connecting to our email address.
I am going to use a ternary operator to check for an error.
transporter.verify((error) => {
error ? console.log(`There was an error for the email connection: ${error}`) : console.log('Ready to send email')
});
Testing the email function
Let's test out sending an email using the good morning message.
We will first create an async function called morningMessage
. Inside that function we will use the sendMail
function to send the good morning message.
We are also going to log a success message along with the messageId
inside the function.
We will then call the morningMessage
function and add a catch for errors.
This is what the complete code looks like.
async function morningMessage() {
let info = await transporter.sendMail(sendMorningMsg)
console.log(`Message send: ${info.messageId}`)
}
morningMessage().catch(console.error);
Now, let's go to the command line and start our server using npm start
.
Log into your email provider, and you should see the message in the inbox.
If you see this message in the console, There was an error for the email connection
, then that means you need to check the transporter object or the values for your email address and password.
If everything checks out, you can stop the server.
Adding node-cron to send emails in the morning
We first have to add the node-cron module to our server.js
file. You can add it to the rest of the modules at the top of the page.
const nodeCron = require('node-cron');
Go back to our code for the morningMessage
function and place that inside a node-cron schedule function.
The schedule()
function takes in a string representation for the scheduled times and a call back function.
nodeCron.schedule("* * * * *", () => {
async function morningMessage() {
let info = await transporter.sendMail(sendMorningMsg)
console.log(`Message send: ${info.messageId}`)
}
morningMessage().catch(console.error);
});
To better understand this syntax, "* * * * *"
let's take a look at this diagram.
# ┌────────────── second (optional)
# │ ┌──────────── minute
# │ │ ┌────────── hour
# │ │ │ ┌──────── day of month
# │ │ │ │ ┌────── month
# │ │ │ │ │ ┌──── day of week
# │ │ │ │ │ │
# │ │ │ │ │ │
# * * * * * *
There are a total of six *
you can use.
The first one is optional and represents seconds.
For example, if you wanted your message to be sent every minute, then you would use * * * * *
.
Go ahead and try to test that and see if your message is sent every minute. Run npm start
in the command line, and you should see your message show up in your inbox.
Then stop the server.
If you want your message to be sent every two minutes then you would use */2 * * * *
.
For the morning message, we want to schedule a message to be sent every morning at 9am.
This is the syntax for the scheduled time "0 9 * * *"
.
The node-cron module uses military time. We are using the number 9 to represent 9am.
Here is the complete code.
nodeCron.schedule("0 9 * * *", () => {
async function morningMessage() {
let info = await transporter.sendMail(sendMorningMsg)
console.log(`Message send: ${info.messageId}`)
}
morningMessage().catch(console.error);
});
Using node-cron to send birthday messages
Underneath the schedule for the morningMessage
, create a new schedule for the birthday message.
nodeCron.schedule("* * * * *", () => {
async function birthdayMessage() {
let info = await transporter.sendMail(sendBirthdayMsg)
console.log(`Message send: ${info.messageId}`)
}
birthdayMessage().catch(console.error);
});
For the schedule, we want to see a message once a year on a friends birthday at 11am.
For example, this is the syntax if your friends birthday is April 17th.
"0 11 17 April *"
That will send once a year on their birthday.
This is what the full birthday message looks like.
nodeCron.schedule("0 11 17 April *", () => {
async function birthdayMessage() {
let info = await transporter.sendMail(sendBirthdayMsg)
console.log(`Message send: ${info.messageId}`)
}
birthdayMessage().catch(console.error);
});
Changing the recipient field in the message objects
When you are finished testing your bot, remember to change the to
field to include your friend's email instead of your own.
to: friendEmail,
To test it out, make sure to start your local server a few minutes before the scheduled morning time.
Then check in with your friend to see if they received it.
You can also add a cc
field to the message object, so you get a copy of the email as well.
cc: personalEmail,
Final code
This is the complete code for our server.js
file.
//imports for the env variables and packages
require('dotenv').config();
const port = process.env.PORT || 3000;
const personalEmail = process.env.PERSONAL_EMAIL;
const emailPassword = process.env.EMAIL_PASSWORD;
const friendEmail = process.env.FRIEND_EMAIL;
const express = require('express');
const app = express();
const nodeMailer = require('nodemailer');
const nodeCron = require('node-cron');
//messages for bot
const morningMsg = "Good morning! Hope you have a beautiful day!";
const birthdayMsg = "Happy Birthday! You rock!!!!";
//message objects
let sendMorningMsg = {
from: personalEmail,
to: friendEmail,
cc: personalEmail,
subject: "It's a beautiful morning",
text: morningMsg
};
let sendBirthdayMsg = {
from: personalEmail,
to: friendEmail,
cc: personalEmail,
subject: "Hooray it's your Birthday",
text: birthdayMsg
};
//transporter to send emails from our account
let transporter = nodeMailer.createTransport({
service: 'outlook',
port: 587,
secure: false,
auth: {
user: personalEmail,
pass: emailPassword
}
});
//verifies a proper email connection
transporter.verify((error) => {
error ? console.log(`There was an error for the email connection: ${error}`) : console.log('Ready to send email')
});
//sends a morning message to our friend at 9am everyday
nodeCron.schedule("0 9 * * *", () => {
async function morningMessage() {
let info = await transporter.sendMail(sendMorningMsg)
console.log(`Message send: ${info.messageId}`)
}
morningMessage().catch(console.error);
});
// sends a message once a year to our friend on their birthday
nodeCron.schedule("0 11 17 April *", () => {
async function birthdayMessage() {
let info = await transporter.sendMail(sendBirthdayMsg)
console.log(`Message send: ${info.messageId}`)
}
birthdayMessage().catch(console.error);
});
//listens for connections
app.listen(port, () => {
console.log(`The server has started at http://localhost:${port}`)
});
Thank you so much for making it to the end of the tutorial. 😄
To learn more about the features for node-cron, please visit the documentation.
To learn more about the features for Nodemailer, please visit the documentation
Nodemailer Project GitHub Repo
Happy coding!
Top comments (4)
A good article, can the complete code be uploaded to github? For self-learning
I just created the github repo.
github.com/jdwilkin4/Nodemailer-bo...
Thanks for reading!
This is brilliant! I've been working on something similar with the same packages. Have you thought about hosting it somewhere?
I am glad you like the article.
I was thinking about doing a follow up article on how to style the emails and host it through Heroku.