How to Automate Emails with Strapi CRON Jobs
Tags: strapi, javascript, cms, webdev
Published: 2022-07-18 15:03:46

---
canonical_url: https://strapi.io/blog/automating-emails-with-strapi-cron-jobs?utm_campaign=Strapi%20Blog&utm_source=devto&utm_medium=blog
---

Build a landing page that allows a company to collect leads in the form of user emails and send automated emails at pre-determined intervals.

**Author: Alex Godwin**

Email automation is a critical component of modern marketing. It allows businesses and enterprises to reach out to a large number of potential customers and to update existing customers on new products and company policies.

Using [Strapi cron tasks](https://docs.strapi.io/developer-docs/latest/guides/scheduled-publication.html#example) to automate emails is a powerful concept. In this tutorial, we'll build a landing page that allows a company to collect leads in the form of user emails and send automated emails at pre-determined intervals.

## Prerequisites

- Basic Knowledge of [Vue.js](https://vue.js.org/),
- Knowledge of JavaScript, and
- [Node.js](https://nodejs.org/) (v14 recommended for strapi).


The completed version of your application should look like the image below:

![Application Sample Screenshot](https://paper-attachments.dropbox.com/s_5956BCB785AF96D54C49E73C4A52A0B6B58F6AB7F700655C466A117B3059D50C_1650727878989_Screenshot+from+2022-04-23+16-28-26.png)

![Sample 2](https://paper-attachments.dropbox.com/s_5956BCB785AF96D54C49E73C4A52A0B6B58F6AB7F700655C466A117B3059D50C_1650727854904_Screenshot+from+2022-04-23+16-28-32.png)


## An Introduction to Strapi

[The Strapi documentation](https://strapi.io/documentation/developer-docs/latest/getting-started/introduction.html) says that "Strapi is a flexible, open-source Headless CMS that gives developers the freedom to choose their favorite tools and frameworks while also allowing editors to manage and distribute their content easily." 

Strapi enables the world's largest companies to accelerate content delivery while building beautiful digital experiences by making the admin panel and API extensible through a plugin system.


## Scaffolding a Strapi Project

[To install Strapi, head over to the Strapi docs at Strapi.](https://docs.strapi.io/developer-docs/latest/getting-started/introduction.html) We’ll be using the SQLite database for this project. To install Strapi, run the following commands:

```bash
    yarn create strapi-app my-project # using yarn
    npx create-strapi-app@latest my-project # using npx
```

Replace `my-project` with the name you wish to call your application directory. Your package manager will create a directory with the specified name and install Strapi.

If you have followed the instructions correctly, you should have Strapi installed on your machine. Run the following commands to start the Strapi development server:

```bash
    yarn develop # using yarn
    npm run develop # using npm
```
The development server starts the app on http://localhost:1337/admin. 



## Building the Mailing-List Collection Type

Let’s create our `Mailing-list` collection type which will hold subscribers email addresses:


1. Click on `Content-Type Builder` under `Plugins`  on the side menu.
2. Under `collection types`, click `create new collection type`.
3. Create a new `collection-type` named `Mailing-list` .
4. Create the following fields under `*product content-type*`*:*
    - `email` as `Email`
    
![Mailing-list collection type](https://paper-attachments.dropbox.com/s_5956BCB785AF96D54C49E73C4A52A0B6B58F6AB7F700655C466A117B3059D50C_1650127939260_Screenshot+from+2022-04-16+17-51-18.png)

## Building the Email-Template Collection Type

Next, we create our `Email-template`  collection type, which will be the model for the Emails we send out:


1. Click on `Content-Type Builder` under `Plugins`  on the side menu.
2. Under `collection types`, click `create new collection type`
3. Create a new `collection-type` named `Email-template` .
4. Create the following fields under `product content-type` :
    - `Subject` as `Text`
    - `Content` as `RichText`
    
![Email-template collection type](https://paper-attachments.dropbox.com/s_5956BCB785AF96D54C49E73C4A52A0B6B58F6AB7F700655C466A117B3059D50C_1650127705870_Screenshot+from+2022-04-16+17-47-29.png)


This will enable us to create the E-mails we send out to our users.


## Introduction to CRON Jobs

Cron Jobs are used for a variety of purposes. Cron Jobs are used to schedule tasks on the server to run. They're most typically used to automate system management or maintenance. They are, nevertheless, also relevant in the building of web applications. A web application may be required to conduct specific operations on a regular basis in a variety of circumstances.


## Why Use Cron Jobs?


- If you have a membership site with expiration dates, you can use cron tasks to deactivate or remove accounts that have passed their expiration dates on a regular basis.
- You have the option of sending out daily newsletter e-mails.
- Scheduling activities that are carried on a regular basis.


## Structure of Cron Jobs


      '* * * * * *'


    *    *    *    *    *    *
    ┬    ┬    ┬    ┬    ┬    ┬
    │    │    │    │    │    |
    │    │    │    │    │    └ day of week (0 - 7) (0 or 7 is Sun)
    │    │    │    │    └───── month (1 - 12)
    │    │    │    └────────── day of month (1 - 31)
    │    │    └─────────────── hour (0 - 23)
    │    └──────────────────── minute (0 - 59)
    └───────────────────────── second (0 - 59, OPTIONAL)


## Setting up Cron Jobs in Strapi

Follow the steps below to set up a cron job in your Strapi application:

1. Open up the `config/server.js` file, then add the following lines of code to the server configuration.

```
    cron: {
        enabled: true,
        tasks: cronTasks,
      }
```
 
 2. Create a `cron-task.js` file in the `config` folder and add the following

```js    
    module.exports = {  
        '01 10 16 * * *': async ({ strapi }) => {
          console.log('cron running')
    }
```    

Creating a cron-job in Strapi is as easy as that.


## Setting up an Email Provider in Strapi

We’ll set up the `@strapi/provider-email-nodemailer`.

1. To install the package run:
```bash
    npm i @strapi/provider-email-nodemailer
```

2. In `config/plugins.js`, add the following:

```js
    module.exports = ({ env }) => ({
        email: {
          config: {
            provider: 'nodemailer',
            providerOptions: {
              host: env('SMTP_HOST'),
              port: env('SMTP_PORT'),
              auth: {
                user: env('SMTP_USERNAME'),
                pass: env('SMTP_PASSWORD'),
              },
              pool: true,
              logger: true,
              debug: true,
              maxConnections: 10000
            },
            
            settings: {
              defaultFrom: env('DEFAULT_EMAIL'),
              defaultReplyTo: env('DEFAULT_EMAIL'),
            },
          },
        },
    });
```

3. Create a `.env` file and add your credentials to it:


    SMTP_HOST=YOUR_SMTP_HOST
    SMTP_PORT=465
    SMTP_USERNAME=YOUR_SMTP_USERNAME
    SMTP_PASSWORD=YOUR_SMTP_PASSWORD
    DEFAULT_EMAIL=YOUR_EMAIL_ADDRESS

You may use any mail service provider of your choice.

Now that we have our email service, we can update our `cron-job.js` file to send emails appropriately.

Open up the `cron-task.js` file and edit it’s content with the following lines of code:

```js
    const marked = require('marked')
    module.exports = {
      
        '01 13 15 * * *': async ({ strapi }) => {
          console.log('cron running')
          try {
            let emails = await strapi.service('api::email-template.email-template').find()
    
            emails = emails.results.reverse()
            
            const subscribers = await strapi.service('api::mailing-list.mailing-list').find()
            
            const content = marked.parse(emails[0].Content)
    
            await Promise.all(subscribers.results.map(async (el, i) => {  
              return await strapi
              .plugin('email')
              .service('email')
              .send({
                to: el.email,
                subject: 'Test mail',
                html: content,
              });
            }))
            
          } catch (error) {
            console.log(error)
          }
        },
      };
```

You can set the CRON job for what ever time you want. Furthermore, what our logic states is that we’ll get the most recent e-mail from our `email-template` and send that to all users in our `mailing-list`.


## Building an Application that Collects User Emails and Add Them to Our Mailing List

Obviously, we need a means to collect actual emails for our mailing-list. We’ll build the front-end of our application using Vue 3. [`Vue` is a JavaScript framework for building user interfaces.](https://vue.js.org/)

Run the following command to install `vue`:
```bash
    mkdir client
    npm init vue@latest
```
Provide appropriate responses to the prompts, then run the following commands:

```bash
    cd <your-project-name>
    npm install
    npm run dev
```
Your `vue` app should be running on the specified port. Open up the `app.vue` file and update it with the following code:

```js
    <script setup>
    import { RouterLink, RouterView } from 'vue-router'
    import HelloWorld from '@/components/HelloWorld.vue'
    </script>
    <template>
      <header>
        <div class="wrapper">
          <HelloWorld msg="Welcome to UoRos" />
          <nav>
            <RouterLink to="/">Home</RouterLink>
            <RouterLink to="/about">About</RouterLink>
          </nav>
        </div>
      </header>
      <RouterView />
    </template>
    <style>
    @import '@/assets/base.css';
    #app {
      max-width: 1280px;
      margin: 0 auto;
      padding: 2rem;
      font-weight: normal;
    }
    header {
      line-height: 1.5;
      max-height: 100vh;
    }
    .logo {
      display: block;
      margin: 0 auto 2rem;
    }
    a,
    .green {
      text-decoration: none;
      color: hsla(160, 100%, 37%, 1);
      transition: 0.4s;
    }
    @media (hover: hover) {
      a:hover {
        background-color: hsla(160, 100%, 37%, 0.2);
      }
    }
    nav {
      width: 100%;
      font-size: 12px;
      text-align: center;
      margin-top: 2rem;
    }
    nav a.router-link-exact-active {
      color: var(--color-text);
    }
    nav a.router-link-exact-active:hover {
      background-color: transparent;
    }
    nav a {
      display: inline-block;
      padding: 0 1rem;
      border-left: 1px solid var(--color-border);
    }
    nav a:first-of-type {
      border: 0;
    }
    @media (min-width: 1024px) {
      body {
        display: flex;
        place-items: center;
      }
      #app {
        display: grid;
        grid-template-columns: 1fr 1fr;
        padding: 0 2rem;
      }
      header {
        display: flex;
        place-items: center;
        padding-right: calc(var(--section-gap) / 2);
      }
      header .wrapper {
        display: flex;
        place-items: flex-start;
        flex-wrap: wrap;
      }
      .logo {
        margin: 0 2rem 0 0;
      }
      nav {
        text-align: left;
        margin-left: -1rem;
        font-size: 1rem;
        padding: 1rem 0;
        margin-top: 1rem;
      }
    }
    </style>
```
Open up the `HelloWorld.vue` component and update it with the following code:

```js
    <script setup>
    defineProps({
      msg: {
        type: String,
        required: true
      }
    })
    </script>
    <template>
      <div class="greetings">
        <h1 class="green">{{ msg }}</h1>
        <h3>
          UoRos is a Futuristic company, invested in making sustainabile energy.
        </h3>
        <h3>
          <strong class="bold">
            Solar + Renewable energy
          </strong>
        </h3>
      </div>
    </template>
    <style scoped>
    h1 {
      font-weight: 500;
      font-size: 2.6rem;
      top: -10px;
    }
    h3 {
      font-size: 1.2rem;
    }
    .bold {
      font-weight: 900;
    }
    .greetings h1,
    .greetings h3 {
      text-align: center;
    }
    @media (min-width: 1024px) {
      .greetings h1,
      .greetings h3 {
        text-align: left;
      }
    }
    </style>
```
In the `TheWelcome.vue` component, place following code:

```js
    <script setup>
    import WelcomeItem from './WelcomeItem.vue'
    import DocumentationIcon from './icons/IconDocumentation.vue'
    import ToolingIcon from './icons/IconTooling.vue'
    import EcosystemIcon from './icons/IconEcosystem.vue'
    import CommunityIcon from './icons/IconCommunity.vue'
    import SupportIcon from './icons/IconSupport.vue'
    </script>
    <template>
      <WelcomeItem>
        <template #icon>
          <DocumentationIcon />
        </template>
        <template #heading>Vision</template>
        Lorem, ipsum dolor sit amet consectetur adipisicing elit. Voluptatibus corrupti eveniet asperiores assumenda, sit quia eligendi porro exercitationem! Vitae ab veniam dolorum voluptates! Iste rerum molestiae nobis tenetur unde odit.
      </WelcomeItem>
      <WelcomeItem>
        <template #icon>
          <ToolingIcon />
        </template>
        <template #heading>Purpose</template>
        Lorem, ipsum dolor sit amet consectetur adipisicing elit. Alias optio, facere velit officiis dolore doloremque ipsa minima, explicabo, fugiat placeat debitis repellat. Assumenda accusantium enim, aspernatur nemo illo ducimus quia.
        <br />
      </WelcomeItem>
      <WelcomeItem>
        <template #icon>
          <EcosystemIcon />
        </template>
        <template #heading>Ecosystem</template>
        Lorem, ipsum dolor sit amet consectetur adipisicing elit. Architecto beatae officia culpa animi quas labore! Ducimus tempora, voluptatibus illo laudantium laborum repudiandae tempore labore fugit at excepturi dolore placeat minus?
      </WelcomeItem>
    </template>
```
Update the `About.vue` component with the following code:

```js
    <template>
      <div class="about">
        <div>
          <h1 class="heading">
            About UoRos
          </h1>
          <p class="desc_text">
            Lorem ipsum dolor sit amet consectetur adipisicing elit. Esse maiores velit molestias assumenda numquam eligendi. Perferendis pariatur, eligendi at nostrum odio ducimus quod consequatur dignissimos culpa magni commodi, quo esse!
          </p>
          <h1 class="heading">
            What we do
          </h1>
          <p class="desc_text">
            Lorem ipsum dolor sit amet consectetur adipisicing elit. Esse maiores velit molestias assumenda numquam eligendi. Perferendis pariatur, eligendi at nostrum odio ducimus quod consequatur dignissimos culpa magni commodi, quo esse!
            Lorem ipsum dolor, sit amet consectetur adipisicing elit. Placeat voluptatibus officia atque fuga nesciunt rem iste ab saepe ducimus, praesentium soluta quis temporibus, tempora voluptas facere quos reprehenderit beatae in!
          </p>
          <h1 class="heading">
            Join our mailing list
          </h1>
          <p class="desc_text">
            Lorem ipsum dolor sit amet consectetur adipisicing elit. Delectus eum assumenda dolor laudantium sed temporibus nulla sunt dicta voluptatibus asperiores harum officiis, at, quibusdam beatae corrupti. Suscipit placeat modi corrupti!
          </p>
          <div>
            <form action="" @submit="signUpToMail">
              <p>{{ error }}</p>
              <input type="email" class="email_input" name="email" placeholder="Email address" id="" v-model="email">
              <input type="submit" class="email_submit" value="Sign up">
            </form>
          </div>
        </div>
      </div>
    </template>
    <style>
    @media (min-width: 1024px) {
      .about {
        min-height: 100vh;
        display: flex;
        align-items: center;
      }
      .heading {
        /* margin: 10px 0; */
        font-weight: 700;
      }
      .desc_text {
        margin: 0 0 15px 0;
      }
      .email_input {
        padding: 12px 10px;
        font-size: 16px;
        border: none;
        background: rgb(232, 240, 254)
      }
      .email_submit {
        background-color: hsla(160, 100%, 37%, 1); /* Green */
        border: none;
        color: white;
        padding: 12px 10px;
        text-align: center;
        text-decoration: none;
        display: inline-block;
        font-size: 16px;
        margin: 0 5px;
      }
    }
    </style>
    <script>
    import axios from 'axios'
    export default {
      data() {
        return {
          email: '',
          error: ''
        }
      },
      methods: {
        async signUpToMail(e) {
          e.preventDefault()
          if(!this.email) return this.error = `Enter an email address`
          const data = {
            data: {email: this.email}
          }
          console.log('Email', this.email)
          try {
            await axios(`http://localhost:1337/api/mailing-lists`, {
              method: 'POST',
              data
            })
            
          } catch (error) {
            console.log(error) 
          }
          
          this.email = ''
          this.error = ''
        }
      }
      
    }
    </script>
```

This component is responsible for the API call to our Strapi server.

> The old Strapi documentation says this: 
`Please note that Strapi's built in CRON feature will not work if you plan to use pm2 or node based clustering. You will need to execute these CRON tasks outside of Strapi.`

### The Problem

You could for example deploy your Strapi application in multiple containers/clusters/instances, to boost the performance ([horizontal scaling](https://stackoverflow.com/a/11715598)). This could for example be in multiple pm2 clusters or Google App engine instances, depending on the hosting.

If you have deployed your Strapi application in multiple instances, then each of those will trigger the cron job and then for example you could have duplicate emails sending because it was triggered & executed in more then one instance.

A solution for this could be to use an third party service that handles the scheduling with for example cron and then uses an custom endpoint to trigger (in one instance) the custom code once.

### Possible Solution

You could create a custom route/controller that would perform the same logic as a crontask (maybe implement API tokens to secure it). Then you would call this endpoint from any other system.

Could be the built in CRON job system in Linux with a simple curl request or using a more complex self hosted cron system. You can even use things like Zapier or IFTTT, doesn't matter what it is so long as it can make the GET, POST, or PUT request you want (usually GET).

This way the "crontask" endpoint will only be executed on one node of the cluster.

## Conclusion

Finally we have our full application, which would enable us collect emails and send them new information about our company regularly at a scheduled time.