AWS Lambda is for backend tasks—for computational grunt work. Right? Yes, but in a pinch it can deliver HTML content, allowing us to build interactive web apps.

Note: This brief guide assumes a modicum of experience about AWS Lambda and Amazon IAM, and Amazon API Gateway. For a more detailed look at this guide’s concepts, check out my Compare OSM Feature Density project.

We start out by creating a Python 3.9 Lambda function in AWS at https://console.aws.amazon.com/lambda.

Next, access the Identity and Access Management console at https://console.aws.amazon.com/iamv2 and add the PowerUserAccess policy to the role automatically created for the Lambda function. The function doesn’t need all that power, but I haven’t yet been able to track down the minimum policies required for Lambda to communicate through a REST API.

Now we need a REST API gateway. Go to https://console.aws.amazon.com/apigateway and create a REST API endpoint with GET and POST methods. When creating the methods, ensure Use Lambda Proxy integration is checked, and that the Lambda function we created is selected in the Lambda function field. Now deploy the API, naming the API stage app.

Returning to https://console.aws.amazon.com/lambda and clicking on our recently created Lambda function, we are greeted with in lambda_function.py in the integrated IDE:

import json

def lambda_handler(event, context):
    # TODO implement
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }

When the API endpoint is accessed, it will send an “event” to our integrated Lambda function, and thus to the lambda_handler Python function. This “event” dictionary will contain two important keys:

  • httpMethod - in our setup, either “GET” or “POST”
  • body - any data entered into a web form will be found here

I’ve updated the function to save these values to snake case variables:

import json

def lambda_handler(event, context):
    http_method = event['httpMethod']
    body = event['body']
    return {
        'statusCode': 200,
        'body': json.dumps('Hello from Lambda!')
    }

The GET method will be used to request data from our function (e.g. an HTML page), and the POST method will be used to send form data back to the function. Dealing with these two methods requires an if statement:

import json

def lambda_handler(event, context):
    http_method = event['httpMethod']
    body = event['body']

    if http_method == 'GET':
        return {
            'statusCode': 200,
            'body': json.dumps('The GET method was used!')
        }
    
    if http_method == 'POST':
        return {
            'statusCode':200,
            'body': json.dumps('The POST method was used!')
        }

For the next step, two HTML files need to be created in the same directory as lambda_function.py. form.html to submit information, and results.html to view returned information.

form.html:

<!DOCTYPE html>
<html>
    <head>
        <title>Lambda web app</title>
        <meta charset="UTF-8">
    </head>
    <body>
        <form action="/app" method="POST">
            <label for="number">Enter a number to square</label>
            <br>
            <input type="text" id="number" name="number">
            <br>
            <input type="submit" value="Calculate">
        </form>
    </body>
</html>

results.html:

<!DOCTYPE html>
<html>
    <head>
        <title>Lambda web app</title>
        <meta charset="UTF-8">
    </head>
    <body>
        <h1>{square}</h1>
    </body>
</html>

Now, we can configure lambda_handler to read these HTML files and return them when the appropriate HTTP method is detected. I’ve also added lines to parse the form response, square the number returned by the form, and use the str.format() method to replace {square} in results.html with the result of the squaring operation.

import json
from urllib import parse as urlparse

def lambda_handler(event, context):
    http_method = event['httpMethod']
    body = event['body']

    if http_method == 'GET':
        html_file = open('form.html', 'r')
        html_content = html_file.read()
        
        return {
            'statusCode': 200,
            'headers': {'Content-Type': 'text/html'},
            'body': html_content
        }
    
    if http_method == 'POST':
        parsed_body = urlparse.parse_qs(body)
        number = int(parsed_body.get("number))
        square = number ** 2

        html_file = open('results.html', 'r')
        html_content = html_file.read().format(square = square)
        
        return {
            'statusCode': 200,
            'headers': {'Content-Type': 'text/html'},
            'body': html_content
        }

The function is now ready to be tested. Under the dashboard of the API you created earlier, you’ll find an endpoint link near the top of the page. Click that and test out your new Lambda-powered web app.

If you have any questions or comments, please send me an email.