Easily send emails in Strapi with any provider
Tags: webdev, node, beginners
Published: 2020-12-01 10:22:22

Sending emails is a fundamental feature in most projects. From welcoming new users to sending notifications and reminders, it's something that I find myself doing in every project.

I've been using [Strapi](https://strapi.io) for a few months now as it allows me to [create APIs easily and fast](https://antonioufano.com/articles/building-apis-fast-with-strapi-an-overview-34) and, even though it **includes a default plugin to send emails out the box, it's not the best option to use in Production**. There are multiple plugins for different email providers but in this article, I'll show you how to **use a plugin that works with any provider**.

## Why you shouldn't use Strapi's default email plugin

Why do we need to install a new plugin if Strapi has one out of the box? As they mention in [the documentation](https://strapi.io/documentation/v3.x/plugins/email.html#programmatic-usage), Strapi uses **[sendmail](https://en.wikipedia.org/wiki/Sendmail)** as a default provider, which basically means that **the emails are sent directly from the local server running Strapi**.

You might use this during development but in Production it can cause you some issues:

- It's not 100% reliable
- Common mail servers, like **Gmail, Outlook and Yahoo will block your emails if they are not being sent from a trusted server**.
- Your server might not have sendmail installed
- Sendmail won't probably work if you are deploying your Strapi application with Docker

To avoid these issues, it's better to use an external provider. In the Strapi documentation they have an example using Sendgrid with its own specific plugin. Although it's a great platform, Sendgrid's free tier is very limited (only 100 emails/day) so I prefer to use [SendinBlue](https://www.sendinblue.com/) (free 300 emails/day) or for more email demanding projects, [ElasticEmail](https://elasticemail.com/) ($0.09 per 1000 emails).

Although there is [a specific plugin for SendinBlue](https://www.npmjs.com/package/strapi-provider-email-sendinblue), I don't like the idea of my application depending on one specific third party like that and I'm not sure if I'll be using the same provider forever, so **I prefer to use a plugin that is provider agnostic**. And what do most email providers have in common? They all support the **SMTP protocol**, so that's what we'll use.

> For this article, I'd assume we'll be using SendinBlue so go ahead and [create a free account](https://app.sendinblue.com/account/register). You can find the SMTP details in your account settings > SMTP & API.

![Sendinblue SMTP connection details](https://antonioufano.com/image_uploads/1606476235503.png)

## Plugin install and setup

> The first thing you'll need is a scaffolded Strapi project. You can check how to create one in [this article](https://antonioufano.com/articles/building-apis-fast-with-strapi-an-overview-34).

The plugin we're going to use is [strapi-provider-email-nodemailer](https://www.npmjs.com/package/strapi-provider-email-nodemailer) and, as I mentioned I choose this one for multiple reasons:

- It's provider agnostic
- Uses SMTP protocol
- Super easy to configure and use
- Migrating to a different provider is super simple

To install it, run `npm i strapi-provider-email-nodemailer`. To configure it I recommend to use an .env file as your server details will probably be different in DEV and PROD. To checkout how to use .env files in Strapi, check [this section of the docs](https://strapi.io/documentation/v3.x/concepts/configurations.html#configuration-using-environment-variables).

In our .env file we'll add the following variables:

```
EMAIL_PROVIDER=nodemailer
EMAIL_SMTP_HOST=smtp.zzzz.zzzzz
EMAIL_SMTP_PORT=587
EMAIL_SMTP_USER=xxXXXXxxxxXXX
EMAIL_SMTP_PASS=yyyYYYYYyyyYYYY
EMAIL_ADDRESS_FROM=aaaaaa@bbbbbb.com
EMAIL_ADDRESS_REPLY=ccccccc@dddddd.com
```

> Sendinblue's SMTP host is `smtp-relay.sendinblue.com` and port is 587.

Next step is to configure the plugin in the _config/plugins.js_ file:

```js
// File: config/plugins.js

module.exports = ({ env }) => ({
  // ...
  email: {
    provider: env('EMAIL_PROVIDER'),
    providerOptions: {
      host: env('EMAIL_SMTP_HOST', 'smtp.example.com'),
      port: env('EMAIL_SMTP_PORT', 587),
      auth: {
        user: env('EMAIL_SMTP_USER'),
        pass: env('EMAIL_SMTP_PASS'),
      },
    },
    settings: {
      defaultFrom: env('EMAIL_ADDRESS_FROM'),
      defaultReplyTo: env('EMAIL_ADDRESS_REPLY'),
    },
  },
  // ...
})
```

As you can see, the configuration is pretty straight forward and we just have to assign the server and authentication details that we included in the env before.

> If you don't want to use an env file, you can populate these variables using `process.env.YOUR_ENV_VARIABLE` instead of `env('YOUR_ENV_VARIABLE')` or, if you're testing locally, directly assigning the values but **remember not to commit these to your repository** for security reasons.

## Sending emails

To make this example as simple as possible, I'll create an endpoint in our API that automatically sends an email. We'll create the following files inside _api_ folder:

- /api/email/config/routes.json

```json
// /api/email/config/routes.json
{
  "routes": [
    {
      "method": "POST",
      "path": "/emails",
      "handler": "Email.send",
      "config": {}
    }
  ]
}
```

This will create a new POST endpoint _/emails_ in our API, and the requests will be handled by the _send_ method inside the Email controller, which we will create next:

- /api/email/controllers/Email.js

```js
// File /api/email/controllers/Email.js
'use strict'

/**
 * Read the documentation () to implement custom controller functions
 */

module.exports = {
  /**
   * Sends an email to the recipient in the body of the request
   */
  send: async (ctx) => {
    const body = ctx.request.body
    const sendTo = body.email
    strapi.log.debug(`Trying to send an email to ${sendTo}`)

    try {
      const emailOptions = {
        to: sendTo,
        subject: 'This is a test',
        html: `<h1>Welcome!</h1><p>This is a test HTML email.</p>`,
      }
      await strapi.plugins['email'].services.email.send(emailOptions)
      strapi.log.debug(`Email sent to ${sendTo}`)
      ctx.send({ message: 'Email sent' })
    } catch (err) {
      strapi.log.error(`Error sending email to ${sendTo}`, err)
      ctx.send({ error: 'Error sending email' })
    }
  },
}
```

The _send_ method receives a POST request with the email in the body. We create an _emailOptions_ object that contains the properties for destination address (to), subject and html as the body of the email. Then we just have to use the the _strapi.plugins['email'].services.email.send_ method and pass it the _emailOptions_ object.

> If you need to add attachements, bcc or other email options, you can check the [nodemailer message configuration here](https://nodemailer.com/message/). You'd just need to add the options you need in the emailOptions object 😉

Now if we start our Strapi application with `npm run develop` and send a POST request to _/emails_ with Postman or cURL, our email will be sent 📬

```sh
# Send POST request to /emails via cURL

curl --location --request POST 'localhost:1337/emails' \
--header 'Content-Type: application/json' \
--data-raw '{"email": "example@email.com"}'
```

## Conclusion

This is a very basic example to show you **how to programmatically send emails with Strapi via SMTP**. In my case I normally trigger emails in cron jobs or in an authentication protected endpoints after users do a certain action.

> **Important**: You shouldn't have a public endpoint in your API that triggers emails as it can be easily exploited and you could be charged by your email provider.

I like using nodemailer because it's **provider agnostic and it allows me to easily change providers** by just changing the server and credential details in my env file. In addition, it supports Amazon SES transport so you can use that option. You can find more info about [how to use nodemailer with SES here](https://nodemailer.com/transports/ses/).

If you liked this article, you can [follow me on Twitter](https://twitter.com/uf4no) where I share dev tips, interesting articles and updates about the progress of my projects 🤙

Happy coding!

* * *

_This article was originally posted in [my blog](https://antonioufano.com/blog) where you can find other articles about web development focused on Node.js, Vue, Laravel and anything related with product development._