Serving Dynamic Web Pages using Python and AWS Lambda

Adam Novotny
3 min readJul 25, 2020

--

While AWS Lambda functions are typically used to build API endpoints, at their core Lambda functions can return almost anything. This includes returning html markup with dynamic content.

AWS Lambda + Python + Jinja

I will not go into details describing how to deploy AWS Lambda functions. Please see the official documentation. I will however describe how to return dynamic html content instead of a typical JSON.

Step 0 — Optional

If you prefer to develop and test lambda functions locally (as I do), you can use Docker to simulate the AWS lambda function environment. A sample Dockerfile I use is below.

FROM amazonlinux:latest
RUN mkdir -p /mnt/app
ADD . /mnt/app
WORKDIR /mnt/app
RUN yum update -y
RUN yum install gcc -y
RUN yum install gcc-c++ -y
RUN yum install findutils -y
RUN yum install zip -y
RUN amazon-linux-extras install python3=3.6.2
RUN pip3 install --upgrade pip
RUN pip3 install -r requirements.txt -t aws_layer/python

The requirements.txt includes just one package for simplicity. It is the common templating for Python called Jinja2

Jinja2==2.11.1

You can test your Lambda function by simple calling it with sample parameters:

import lambda_function
event = {
"queryStringParameters": {
"param1": "value1"
},
"path": "/api",
"requestContext": {
"param2": "value2"
}
}
res = lambda_function.lambda_handler(event=event, context={})
assert 200 == int(res["statusCode"])

Step 1 — Write html template

In this step, we write the html template the Lambda function will return. A good default is the new Bootstrap 5 CSS framework where the recommended starting markup looks something like this:

Sample HTML page

Saving this file in folder “templates” and naming it index.html, we are ready to write the Lambda function.

Step 2 — Write Lambda function to serve your html page

In the example below, the lambda function expects URL parameters and parses those. So when parsing a custom URL, the format would look something like this: Example.com/?my_name=somename. See step 10 in this tutorial to add custom URLs to your API Gateway-triggered Lambda functions.

import os
import sys
from jinja2 import Environment, FileSystemLoader
def lambda_handler(event, context):
env = Environment(loader=FileSystemLoader(os.path.join(os.path.dirname(__file__), "templates"), encoding="utf8"))
my_name = False
if event["queryStringParameters"] and "my_name" in event["queryStringParameters"]:
my_name_query = event["queryStringParameters"]["my_name"]
template = env.get_template("index.html")
html = template.render(
my_name=my_name_query
)
return response(html)
def response(myhtml):
return {
"statusCode": 200,
"body": myhtml,
"headers": {
"Content-Type": "text/html",
}
}
  • jinja2 loads your previously created index.html using class “FileSystemLoader” and we store it as variable “env”
  • variable “my_name” is parsed from the URL query parameters as explained above and stored as the Python variable my_name_query
  • the jinja2 render function then passes my_name_query to the template and returns the html page

Also published on adamnovotny.com

--

--