Collect Serverless Email Sign Ups with a Simple AWS Lambda

June 11, 2021

Collect Serverless Email Sign Ups with a Simple AWS Lambda

I run my project Epilocal on Gatsby and AWS. 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:

const https = require('https');
const JsonResponse = require('./response');

const defaultOptions = {
    host: '',
    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));

exports.handler = async (event) => {
    const { queryStringParameters } = event;
    let postData = { "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);
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 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.

Originally published at Dev.To

Related Posts

Serverless Subscription Management with Fauna, Paddle, Gatsby and Netlify
Storing and Serving Images from Ghost in Amazon S3
How to Build a Gatsby Static Site Using Ghost as a Headless CMS
Checklist for Deploying Gatsby on AWS S3 with Cloudfront