Collect Serverless Email Sign Ups with a Simple AWS Lambda
Tags: serverless, aws, lambda, gatsby
Published: 2021-06-10 14:23:51

I run my project [Epilocal](https://www.epilocal.com/) on [Gatsby and AWS](https://www.epilocal.com/developers/checklist-for-deploying-gatsby-on-aws-s3-with-cloudfront/). Static sites are great for a lot of reasons, but one of the draw-backs they have is that it's more difficult to handle tasks that would typically be done on the server, like form submissions.

One of the most common situations for this is when you are collecting email sign ups on your site.  In a typical set-up with a server, you would be sending your form submission to your server and your server would have a PHP script that would hit the API of your email marketing provider.  

But in a serverless set-up, you don't have that option.  You might be tempted to just call the email marketing service directly, but you shouldn't be exposing the API key for your email service on the client-side, and many email services prevent you from calling their API from the front-end anyway.

There are some services that have started to pop up and fill this need, but it is pretty easily solved with an AWS Lambda.

Here's the simple Lambda that I use on the Epilocal site:

```javascript
//index.js
const https = require('https');
const JsonResponse = require('./response');

const defaultOptions = {
    host: 'api.mailerlite.com',
    port: 443,
    headers: {
        'Content-Type': 'application/json',
        'X-MailerLite-ApiKey': 'YOUR_API_KEY',
    }
}

const post = (path, payload) => new Promise((resolve, reject) => {
    const options = { ...defaultOptions, path, method: 'POST' };
    const req = https.request(options, res => {
        let buffer = "";
        res.on('data', chunk => buffer += chunk)
        res.on('end', () => resolve(JSON.parse(buffer)))
    });
    req.on('error', e => reject(e.message));
    req.write(JSON.stringify(payload));
    req.end();
})

exports.handler = async (event) => {
    const { queryStringParameters } = event;
    let postData = { "email": queryStringParameters.email };
    if (queryStringParameters.form_type) {
        postData["fields"] = {"form_type": queryStringParameters.form_type}
    }
    const response = await post("/api/v2/groups/YOUR_GROUP_NUMBER/subscribers", postData);
    let lamdaResponse = new JsonResponse(200, response);
    return lamdaResponse.build();
};
```

```javascript
//response.js
class JsonResponse {
  constructor(statusCode, message) {
    this.statusCode = statusCode;
    this.message = message;
  }

  build() {
    return {
      statusCode: this.statusCode,
      headers: {
        "Access-Control-Allow-Origin": "*",
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        statusCode: this.statusCode,
        message: this.message,
      }),
    };
  }
}

module.exports = JsonResponse;
```
You can see the file `response.js` is just some boilerplate for building a JSON response.

All of the action is happening in `index.js`. And even here, `defaultOptions` and the `post` function are just boilerplate for creating a POST request. 

So the only code you really need to pay close attention to is in `exports.handler.`  Here we are parsing the Lambda's `queryStringParameters` for an "email" parameter and saving that to a new object called `postData`. Then we are checking if the parameter "form_type" has been provided and if so, we save that to the `postData` object as well.

Then we are simply passing the `postData` object into our `post` function.  Note, we are using `await` here, so `exports.handler` needs to be an `async` function.

Finally, we are taking the response that we get from our email marketing service and passing that to our boilerplate JSON response imported from `response.js`.

I use [Mailerlite](https://www.mailerlite.com/invite/a05b17d3c6dc5) but you could easily adapt this to whatever email marketing service you use, as long as they have a similar API for adding new subscribers.