Send automated reports using Nodejs
Tags: node, devops, javascript
Published: 2019-10-01 06:40:35

## Introduction
In this article we will explain how you can send reports automatically and periodically using a NodeJs  whether you embed the logic inside your application or you make it a separate script its up to the developer but separate script is recommended(we use this approach), before you start you should know what is NodeJs you can benefit from this [A Quick Guide to Node.js in 2019](https://www.vultr.com/docs/a-quick-guide-to-node-js-in-2019) 

## Requirements 
 Two packages is required for this tutorial 


+ `node-cron` used for scheduling tasks
+  `node-mailer` used to send emails to some user list using some SMTP providers googles Gmail will be used for this purpose as it is free and easy to setup

## Project setup
 Start by creating new NodeJs project


1. create a folder & cd to the folder using command line something like `exampleFolder`
2. run `npm init` this will ask you couple of questions after you finish there will be a file called `package.json` alternatively if you don't want to answer questions type `npm init -y` this will answer yes for all question, 

3. install `node-cron` type `npm i node-cron --save` in the console
4. install `nodemailer` type `npm i nodemailer --save` in the console

After the installation you should see a folder called `node_modules` and generated `package.json` should look something like this 

 ```json   
     {
       "name": "exampleFolder",
       "version": "1.0.0",
       "description": "",
       "main": "index.js",
       "scripts": {
          "test": "echo \"Error: no test specified\" && exit 1"
        },
        "keywords": [],
        "author": "",
        "license": "ISC",
        "dependencies": {
           "node-cron": "^2.0.3",
           "nodemailer": "^6.3.0"
        }
    }
```

## Let's start coding 
 We need an entry point for our application(script) start by creating a file called `main.js` and add `"start": "node main.js"` line to `package.json` under `"scripts"` property so it becomes like this 

```json    
     {
       "name": "exampleFolder",
       "version": "1.0.0",
       "description": "",
       "main": "index.js",
       "scripts": {
          "test": "echo \"Error: no test specified\" && exit 1",
          "start": "node main.js"
        },
        "keywords": [],
        "author": "",
        "license": "ISC",
        "dependencies": {
           "node-cron": "^2.0.3",
           "nodemailer": "^6.3.0"
        }
    }

```

Add `console.log("Hello world")` inside `main.js` then in the console type `npm start` this should output `Hello world ` to the console this is used to make sure our application runs
    
### Configure nodemailer
We will start by configuring nodemailer follow these steps 

+ create a file called `reportSender.js`
+ paste this script inside the file 

```javascript
        const nodemailer = require('nodemailer');
        const defaultMailingList = "example1@vultr.com,example2@vultr.com";
        const senderEmail = "sender.example@gmail.com";
        const senderPassword = "gmail_app_password"; // gmail app password
        module.exports = {
            sendMail: async (subject, text, to = defaultMailingList) => {
                try {
                const transporter = nodemailer.createTransport({
                    service: 'Gmail',
                    auth: {
                    user: senderEmail,
                    pass: senderPassword,
                    },
                });

                const message = {
                    from: `report sender <${senderEmail}>`,
                    to,
                    subject,
                    text: subject,
                    html: text,
                };

                transporter.sendMail(message, () => {});
                } catch (e) {
                // handle errors here
                }
            },
        };
```

Note that nodemailer provides handful of transports including custom ones 

Values are hardcoded i would recommend using something like `dotenv` which loads `.env` file and creates environment variables based on the environment variable but for sake of simplicity we did that here,

What this file does is that it exports an object with key `sendMail` which is a function that configures node mailer and setup a generic method for sending our reports as we please for future uses you can add extra keys to main object for example you want the report to be sended via telegram by a bot you can add the logic in this file 

you can get gmail app password following this link [get app password](https://support.google.com/mail/answer/185833?hl=en)

#### Test if node mailer can send emails for us 
+ after you change these variables in a correct way [`senderEmail`,`senderPassword`,`defaultMailingList`]
+ open `main.js` and import `sendMail` from `reportSender.js` using this syntax `const {sendMail} = require('./reportSender')` note that no file extension is required when importing

+ make a call to `sendMail` function so it can send and email example `sendMail("Hello world", "this is email body it can contain html also")` if you didnt update `defaultMailingList` to contain your email you can pass the email after msg body

 the complete body of our `main.js` is something like this 
```js

            const {sendMail} = require('./reportSender');

            console.log('sending email...')
            sendMail("Hello world", "this is email body it can contain html also")
            console.log('email sent ✓')
```


![received email](https://thepracticaldev.s3.amazonaws.com/i/0oqe9pok7g9x4o3kazqf.png)

With this we can send emails to some specific emails as you can predict that you can somehow generate a string which contains a valid html and this html is generated using data from our database then you can send reports or even data to your mailing list
if you are wondering what is a good module for database connection i would recommend using `kenxjs`
the only thing remains is to automate sending reports(emails) with that comes `node-cron` module

## Configure node-cron

Configuring node-cron is an easy task you tell it when do you want for some action to take place then you provide the action definition and whenever the call triggers it will call this action, ction is a function
  
for sake of this tutorial let's say i want to receive a report every 1 min
what you can do according to their [documentation](https://www.npmjs.com/package/node-cron) is something like this 
```js

        var cron = require('node-cron');
        cron.schedule('* * * * *', () => {
            console.log('running a task every minute');
        });
```
You can test this code just comment everything inside `main.js` then wait for 1 min and you should see `running a task every minute` output every 1 min on the console as you can see the callback is our action and its the place that we want to implement and generate our reports then send the emails (generated reports)

## Combine our nodemailer and node-cron 
As you can see by combining just two modules we could make an automated system that can send reports or even anything that we want periodically so lte's show our final `main.js`

```js
        const {sendMail} = require('./reportSender');
        var cron = require('node-cron');
        let counter = 1;
        cron.schedule('* * * * *', () => {
            /* 
                generate your report here then send the report using 
                any reportSender logic that you implemnted email, telegram bot,...
            */
            sendMail(`hello world ${counter}`, "this is email body it can contain html also");
            counter++;
        });
```
## Final words
The idea have alot of potential so you could get creative and make anything you want, the reason i recommended making this as a separate script is that it become another process on the CPU so simply terminate the process if you want to stop the service, in production you have your separate system this script works as an assistant and provides a nice simple feature it could be run using something like `pm2` which is strongly recommend.