Sending Email with Netlify Functions
Tags: nuxt, netlify, serverless, tutorial
Published: 2019-11-04 14:28:06

Functions, lambdas, serverless. Maybe you've heard of these. Maybe you haven't. These words are being used to describe a different way of connecting your frontend code to backend functionality. Without having to run a full-blown backend app server. Or even manage infrastructure. We're empowering front-end developers to be able to accomplish backend tasks like sending an email or making a call to another third-party API by creating and deploying serverless functions. Netlify abstracts away even more complexity for us and uses AWS Lambda behind the scenes. Our Netlify functions can live alongside our site and make use of the same deployment process, making it easier for us to manage, support, and operate.

A prime use case for a serverless function is to send a text message or an email. Here we'll create a contact form that sends you an email using a serverless function. We'll use our `starter-for-nuxt-markdown-blog` from [here](https://github.com/jennapederson/starter-for-nuxt-markdown-blog/tree/setup-markdown-for-nuxt-blog) and work through the following steps:

- Install the Netlify CLI
- Update netlify.toml
- Create the function
- Setup a mailer service
- Send the email
- Try it out locally
- Build the contact page to call the function
- Configure environment variables in Netlify
- Try it out live

And end up with [this](https://starter-for-nuxt-markdown-blog.netlify.com/contact).

Or if you're impatient, grab the final version from the GitHub repo [here](https://github.com/jennapederson/starter-for-nuxt-markdown-blog/tree/send-contact-email-function).

## Install the Netlify CLI

First, we'll install the Netlify CLI. This will allow us to run our functions locally, from the command line alongside our app.

`yarn global add netlify-cli`

## Update netlify.toml

Update your netlify.toml file by adding `functions = "functions"` to the `[build]` section. If you don't already have a `[dev]` section, also add that with `command = "yarn dev"`.

Now you can start up your app with `netlify dev` where it'll run the same `yarn dev` command as before, but it'll also serve your functions in the functions directory and run a proxy server on port 8888 so both your site and functions are on the same port.

But now we need a function!

## Create the function

Run this to create a stub hello world function:

`netlify functions:create send-contact-email`

Select the `[hello-world]` template. This will create `functions/send-contact-email/send-contact-email.js` with a basic hello world template.

To test this right now from the command line, run:

`netlify functions:invoke send-contact-email --no-identity`

Or point your browser at [http://localhost:8888/.netlify/functions/send-contact-email](http://localhost:8888/.netlify/functions/send-contact-email)

You should see the following response:

`{"message":"Hello World"}`

## Setup a mailer service

There are many good players in this space. I love [Postmark](https://postmarkapp.com/) (because of their UI, templates, and reliability) and [Sendgrid](https://sendgrid.com/), but Mailgun also works and is cheap for our purposes (aka has a free tier), so we'll start there.

First, head over to [Mailgun](https://signup.mailgun.com/new/signup) and create your account.

Now we'll need to wire up Mailgun to send the email to us when that function is triggered.

We'll use `dotenv` to access environment variables in our local environment and configure Netlify to use the right values in production.

Install the `dotenv` and `mailgun-js` dependencies:

`yarn add dotenv mailgun-js`

Create a file in the project root called `.env` with the following, replacing these values with those in your own Mailgun account:

    MAILGUN_API_KEY=key-SOME_MAILGUN_KEY
    MAILGUN_DOMAIN=sandboxSOME_MAILGUN_SANDBOX_DOMAIN.mailgun.org
    MAILGUN_URL=https://api.mailgun.net/v3/sandboxSOME_MAILGUN_SANDBOX_DOMAIN.mailgun.org

    FROM_EMAIL_ADDRESS=YOUR_EMAIL_ADDRESS
    CONTACT_TO_EMAIL_ADDRESS=YOUR_EMAIL_ADDRESS

DO NOT COMMIT this file to version control! Make sure you've added a rule to .gitignore. This file is meant to contain sensitive information and should ONLY live on your computer.

## Send the email

Then replace your `send-contact-email.js` file with [this](https://github.com/jennapederson/starter-for-nuxt-markdown-blog/blob/send-contact-email-function/functions/send-contact-email/send-contact-email.js):
```javascript
    require('dotenv').config()
    const { MAILGUN_API_KEY, MAILGUN_DOMAIN, MAILGUN_URL, FROM_EMAIL_ADDRESS, CONTACT_TO_EMAIL_ADDRESS } = process.env
    const mailgun = require('mailgun-js')({ apiKey: MAILGUN_API_KEY, domain: MAILGUN_DOMAIN, url: MAILGUN_URL })

    exports.handler = async (event) => {
      if (event.httpMethod !== 'POST') {
        return { statusCode: 405, body: 'Method Not Allowed', headers: { 'Allow': 'POST' } }
      }

      const data = JSON.parse(event.body)
      if (!data.message || !data.contactName || !data.contactEmail) {
        return { statusCode: 422, body: 'Name, email, and message are required.' }
      }

      const mailgunData = {
        from: FROM_EMAIL_ADDRESS,
        to: CONTACT_TO_EMAIL_ADDRESS,
        'h:Reply-To': data.contactEmail,
        subject: `New contact from $`,
        text: `Name: $\nEmail: $\nMessage: $
      

      return mailgun.messages().send(mailgunData).then(() => ({
        statusCode: 200,
        body: "Your message was sent successfully! We'll be in touch."
      })).catch(error => ({
        statusCode: 422,
        body: `Error: $
      ))
    }
```
A few things to note:

The `require('dotenv').config()` line loads the dotenv environment so that we can access environment variables via `process.env` as you can see on lines 1 and 2.

On line 3, we're initializing the Mailgun client with our environment variables.

On lines 6-13, we're doing some additional validation to make sure it's the right HTTP method (post) and that the function has been passed the correct data.

Lines 15-21 prepare the data we need to pass to Mailgun such as the from/to email address, a reply-to email, a subject line, and the actual message (text for now).

Lines 23-29 sends the email, handles a successful response from Mailgun or an error response from Mailgun.

### Why are we using a server-side function rather than shoving this code right into our front-end code?

We use a server-side function for security purposes. Our client-side code is readable by anyone and we don't want to expose our Mailgun api key to the world. So we push it to a server.

## Try it out locally

If you haven't restarted since creating the `.env` file or changing those values, you'll need to restart with `netlify dev`.

You won't be able to hit it from a browser anymore because we locked it down to POSTs, but that's always a good test case to try!

You can use the command line:

`netlify functions:invoke send-contact-email --no-identity`

This will produce another error case: `Name, email, and message are required.`

Add the required parameters with the `--payload` option:

```bash
netlify functions:invoke send-contact-email --no-identity --payload '{"contactEmail" : "jenna@example.com", "contactName" : "Jenna", "message" : "hello world from a function!"}'
```

You should see `Your message was sent successfully! We'll be in touch.` at the command line and an email in your inbox.

## Build the contact page to call the function

Now we need to make a contact form that triggers the function so that visitors to our site can contact us.

We'll use Axios to make our call to the function, so let's add that package:

`yarn add @nuxtjs/axios`

And configure it in `nuxt.config.js`:

```javascript
    ...
    modules: [
    	...
      '@nuxtjs/axios'
    ],
    axios: {
      baseURL: '/'
    },
    ...
```

Then, we'll create a new page `pages/contact.vue` with a form like [this](https://github.com/jennapederson/starter-for-nuxt-markdown-blog/blob/send-contact-email-function/pages/contact.vue):

```javascript
    <template>
      <div class="container">
        <h1 class="title">
          Contact
        </h1>

        <article v-for="msg in messages"
          :key="msg.text"
          class="message"
          :class="msg.type === 'success' ? 'is-success' : 'is-danger'">
          <div class="message-body">
            {  }
          </div>
        </article>

        <section class="contact-form">
          <div class="field">
            <label class="label">Name</label>
            <div class="control">
              <input v-model="contactName" class="input" type="text">
            </div>
          </div>

          <div class="field">
            <label class="label">Email</label>
            <div class="control">
              <input v-model="contactEmail" class="input" type="email">
            </div>
          </div>

          <div class="field">
            <label class="label">Your Message</label>
            <div class="control">
              <textarea v-model="contactMessage" class="textarea" />
            </div>
          </div>

          <div class="field is-grouped">
            <div class="control">
              <button class="button is-link" @click="sendMessage">
                Send Message
              </button>
            </div>
            <div class="control">
              <button class="button is-text" @click="cancelMessage">
                Cancel
              </button>
            </div>
          </div>
        </section>
      </div>
    </template>

    <script>
    export default {
      data () {
        return {
          messages: [],
          contactName: '',
          contactEmail: '',
          contactMessage: ''
        }
      },
      methods: {
        sendMessage () {
          this.messages = []
          this.triggerSendMessageFunction()
        },
        cancelMessage () 
          
        ,
        resetForm () {
          this.messages = []
          this.contactName = ''
          this.contactEmail = ''
          this.contactMessage = ''
        },
        async triggerSendMessageFunction () {
          try {
            const response = await this.$axios.$post('/.netlify/functions/send-contact-email', {
              contactName: this.contactName,
              contactEmail: this.contactEmail,
              message: this.contactMessage
            })
            this.resetForm()
            this.messages.push({ type: 'success', text: response })
          } catch (error) {
            this.messages.push({ type: 'error', text: error.response.data })
          }
        }
      }
    }

    </script>
```

Now, with `netlify dev` running, point your browser at `localhost:8888/contact`. Be sure to use the proxy port 8888 for the site as that is the same port that our functions are running on.

Let's run through what's in that file.

The template is typical Vue stuff where we show error and success messages on lines 7-14 and for each input, we bind to a data property using `v-model`. We've also got a typical `@click` handler on each button.

In the script block, we set up our `data` on lines 56-63 with an array for error/success messages and the form data we want to submit to our function.

The most interesting part here is the `async triggerSendMessageFunction` on lines 78-90. Here we're using Axios to make our POST call to our new function, passing it our form data. As we learned earlier, our function will live in our `functions` directory inside Netlify's `.netlify` directory. Based on our response, success or error, we also add to the `messages` array.

Give it a whirl and check to see that you've got an email in your inbox! Or that the error handling works as you would expect.

## Configure environment variables in Netlify

A couple more steps and this will be live! Assuming you've already got your site deploying to Netlify on push to your master branch, you'll need to set up the environment variables that we used in our `.env` file.

In your Netlify account, navigate to the Build & Deploy - Environment page for your site.

Add these keys and the production values you want to use. Ideally, you would use the sandbox keys for your local development and testing purposes and a real Mailgun domain for production, but to test this out, you can use the sandbox version here as well.

    MAILGUN_API_KEY=
    MAILGUN_DOMAIN=
    MAILGUN_URL=

    FROM_EMAIL_ADDRESS=
    CONTACT_TO_EMAIL_ADDRESS=

After adding these environment variables, you'll need to trigger a redeploy of your functions so that they are picked up. You can do this on Netlify's Deploys tab in the Trigger Deploy dropdown.

## Try it out live

Give it a whirl!

Hopefully, this gets you started with Netlify functions! You can grab this version of the code from [here](https://github.com/jennapederson/starter-for-nuxt-markdown-blog/tree/send-contact-email-function).

*Ready to get started with this as your starter on Netlify right now? You can do that too!*

[![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/jennapederson/starter-for-nuxt-markdown-blog)