Go to main contentGo to footer
aws
|
24 May 17

How to send email from a static site with AWS Lambda and SES

Static sites are more secure, faster and need less maintenance. But what happens when we want to build a custom mailer instead of using an off-the-shelf one?

The fastest, easiest solution is AWS Lambda + SES (Simple Email Service).

As the official documentation puts it:

AWS Lambda lets you run code without provisioning or managing servers. AWS Lambda runs your code only when needed and scales automatically, from a few requests per day to thousands per second.

Lambda runs the code (and its dependencies) written in Functions. We can write the functions directly in Lambda through the AWS console, or use a service that lets us work in our favorite editor and uploads them for us. Serverless does exactly what we need.

OK, enough introductions: let's see how it works.

Setting up Serverless

First of all, let's make sure Serverless is installed with npm install serverless -g, and create our project:

serverless create --template aws-nodejs --path email_sender

Our project is now ready, and we need to provide AWS IAM access credentials so that Serverless has permission to upload.

Our credentials go in ~/.aws/credentials:

[default]
aws_access_key_id=**************
aws_secret_access_key=***************

In this case Serverless will obviously use the default credentials, but if we wanted to create a new profile we could specify which one to use by running export AWS\_PROFILE="profileName" && export AWS\_REGION=eu-west-1 from inside the Serverless project.

Once we open the project, we can see 3 files: .gitignore, handler.js and serverless.yml

Serverless.yml defines the structure of our project:

service: email-sender

provider:
  name: aws
  runtime: nodejs4.3

functions:
  send:
    handler: handler.send
    events:
      - http:
          path: submissions
          method: post
          response:
            headers:
              Content-Type: "text/json"
          cors:
            origins:
              - '*'

In this file we can also manage our dependencies and specify which packages or plugins will be uploaded to Lambda:

package:
  exclude:
    - node_modules/**
  include:
    - node_modules/serverless-offline/**

plugins:
  - serverless-offline

Note: serverless-offline is an essential plugin that lets you use the service you're writing by sending requests to localhost, without having to upload the Lambda function every time to test it. Install serverless-offline with npm install serverless-offline -g.

The handler.js file is where we write the code we want to run. In serverless.yml, instead, we declare which methods run when we call specific functions. In our case, our method in handler.js will be called ‘send’:

'use strict';
var https = require("https");
var http = require("http");
var AWS = require('aws-sdk');

module.exports.send = (event, context, callback) => {};

We're now ready to deploy our function.

Deploying to AWS Lambda

serverless deploy uploads the function to AWS Lambda and returns the endpoints to call the method we just wrote.

endpoints:

  POST - https://xxxxxx.execute-api.us-east-1.amazonaws.com/dev/submissions

functions:

  email-sender-dev-send: arn:aws:lambda:us-east-1:32xxxxxx:function:email-sender-dev-send

Adding SES

The last thing we need to set up is our email service.

var ses = new AWS.SES();
ses.sendEmail(params, function(err) {
    callback(null, {
      statusCode: 200,
      headers: {
        "Access-Control-Allow-Origin": "*"
      },
      body: JSON.stringify({ status: "success" })
    });
  })

Sending our first email

Our handler.js is now ready to send our first email:

'use strict';
var https = require("https");
var http = require("http");
var AWS = require('aws-sdk');
var ses = new AWS.SES();

module.exports.send = (event, context, callback) => {
  const data = JSON.parse(event.body);

  const params = {
    Destination: {
      ToAddresses: [ "receiver@example.com" ],
    },
    Message: {
      Subject: {
        Data: data.subject,
        Charset: 'UTF-8'
      },
      Body: {
        Text: {
          Data: data.text,
          Charset: "UTF-8"
        }
      }
    },
    Source: "sender@example.com"
  };

  ses.sendEmail(params, function(err) {
    callback(null, {
      statusCode: 200,
      headers: { "Access-Control-Allow-Origin": "*" },
      body: JSON.stringify({ status: "success" })
    });
  })
};

We make a curl POST request to our endpoint and receive our email!

curl -H "Content-Type: application/json" -X POST -d '{subject: "test email", text: "this is a test email"}' https://xxxxxx.execute-api.us-east-1.amazonaws.com/dev/submissions

PS: If you're using the SES sandbox, you need to verify both email addresses, the sender's and the recipient's, as the documentation explicitly states:

footer