Python powered contact form for static site using AWS lambda

Posted by Alasdair Nicol on Sun 08 January 2017

When I switched this site from Django to Pelican in January last year, I chose Parse to handle my contact form, and Mandrill to send the email. Within a couple of months, Facebook had announced that they were closing Parse, and Mailchimp decided to stop the free tier for Mandrill. So much for static sites being easier to maintain!

This week, I've updated the contact form to use AWS. The new form works as follows.

  1. JavaScript on my contact form submits to the API Gateway
  2. The API gateway calls my Python lambda function
  3. The lambda function uses boto3 to send the message to me using SES

Setting up the components through the web interface and connecting them together would be time consuming, so there a number of frameworks that aim to simplify the process. I tried out Serverless and Gordon, before finally settling on Chalice.

Here's a brief summary of my experience. It's not intended to be a comprehensive review, and your experience may vary. I think some of the difficulties I found were because I'm not very familiar with the API gateway, in particular the integration response and method response steps. As a Python developer, I had a bias towards libraries written in Python (or at least with lots of examples in Python), because it prevents the friction of converting examples written in JavaScript.

The first library I look at was Serverless. It's written in JavaScript and although it supports Python lambda functions, I found there wasn't as much documentation for this. In particular, I struggled to map an exception in Python, for example raise ValueError('Please enter your email'), into a JSON response containing the error message.

Next I looked at Gordon, which appealed because it's written in Python and has lots of examples. The biggest problem was that I couldn't get CORS working. There is an open pull request for this, but at the time of writing, the last commit was three months ago, so I don't know when it might be merged.

Finally, I tried Chalice. It's written in Python, and all the configuration was done by decorating my function, rather than yaml config. CORS support was as easy as using cors=True in the decorator. It has a neat experimental feature which introspects the lambda function and generates an IAM policy with the correct permissions (in this case sending the email with SES).

You can find the code for my contact form on GitHub, and you can see it in action by going to my contact page and sending me a message!