Building a Serverless Website on AWS - Part 4

Project Date February 14th, 2024
Code Available at lakshminkmeda.github
Tools AWS, Github, Python

Welcome to the final part of building a serverless website on AWS. This part provides the functionality for the 'Get in Touch' section of the website. By using an API Gateway, Lambda, DynamoDB and SES(Simple Email Service), whenever the form is submitted on the website, the info is saved in a DynamoDB database and an email is sent to the pre-configured email address using SES.

Note: I went for a solution develped by James Beswick to speed up the development and to complete the challenge.


Step 1: Verify Email in Amazon SES

The first step is to register the email address that will be uses to send emails to. The following image shows the SES screen which requires just the email id to be filled.



A verification email will be sent to the email address which needs to be actioned upon. It takes 24hrs for the email to gain 'Production Access granted' status.

Note: SES is a regional service, hence make sure the email is registered in the same region to where the AWS resources are being deployed.


Step 2: Fork the code from Github

Github gives the functionality to download and work on a publicly available code instantly by using the 'Fork' function. I've forked the Serverless_Form_Handler code by James and made minor changes to suit my AWS deployment.

The following changes were made:

  • Change the email parameter in 'template.yaml'
  • Change the region to the region of deployment in 'submitFormFunction/ses.js'
  • Optional: Change the message subject line in 'submitFormFunction/ses.js'
  •                 /*! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
                    *  SPDX-License-Identifier: MIT-0
                    */
                    'use strict'
    
                    const AWS = require('aws-sdk')
                    AWS.config.update({ region: process.env.AWS_REGION || 'XXXXXXXX' })   -- CHANGE REGION HERE
                    const SES = new AWS.SES()
    
                    const sendEmail = async function (formData) {
    
                    const getContent = (formData) => {
                        let retval = ''
                        for (var attribName in formData){
                        retval += attribName + ': ' + formData[attribName] + '\n\n'
                        }
                        return retval
                    }
    
                    return new Promise(async (resolve, reject) => {
    
                        // Build params for SES
                        const emailParams = {
                        Source: process.env.ValidatedEmail, // SES SENDING EMAIL
                        ReplyToAddresses: [process.env.ValidatedEmail],
                        Destination: {
                            ToAddresses: [process.env.ValidatedEmail], // SES RECEIVING EMAIL
                        },
                        Message: {
                            Body: {
                            Text: {
                                Charset: 'UTF-8',
                                Data: getContent(formData)
                            },
                            },
                            Subject: {
                            Charset: 'UTF-8',
                            Data: 'New Form Submission'    -- CHANGE EMAIL SUBJECT LINE HERE
                            },
                        },
                        }
                        // Send the email
                        try {
                        const result = await SES.sendEmail(emailParams).promise()
                        console.log('sendEmail result: ', result)
                        resolve()
                        } catch (err) {
                        console.error('sendEmail error: ', err)
                        reject()
                        }
                    })
                    }
    
                    module.exports = { sendEmail }
                

    The code is ready for deployment which can be directly from a local machine or by using Github Actions which will deploy the resources in the AWS environment. I've resorted to deploying from my local machine to reduce free tier usage limit on my Github account. As this is a one-time deployment, doing it from a local machine would suffice.



    Step 3: Script to call the API from the Website

    The last step is to write a code to call the API whenever the 'Get in Touch' form is filled and submitted. The following script file is added to the list of website files to achieve the functionality.


            (() => {
                const form = document.querySelector('form');
                const submitResponse = document.querySelector('#response');
                const formURL = 'https://XXXXXXXX.execute-api.eu-north-1.amazonaws.com/Prod/submitForm';  -- API GATEWAY INVOKE URL HERE
            
                form.onsubmit = e => {
                e.preventDefault();
            
                // Capture the form data
                let data = {};
                Array.from(form).map(input => (data[input.id] = input.value));
                console.log('Sending: ', JSON.stringify(data));
                submitResponse.innerHTML = 'Sending...'
            
                // Create the AJAX request
                var xhr = new XMLHttpRequest();
                xhr.open(form.method, formURL, true);
                xhr.setRequestHeader('Accept', 'application/json; charset=utf-8');
                xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
            
                // Send the collected data as JSON
                xhr.send(JSON.stringify(data));
            
                xhr.onloadend = response => {
                    if (response.target.status === 200) {
                    form.reset();
                    submitResponse.innerHTML = 'Message sent. Success!';
                    } else {
                    submitResponse.innerHTML = 'Error! Please try again.';
                    console.error(JSON.parse(response));
                    }
                };    
                };
            })();
                

    The finals step is to add the script to the form attribute in the html file. The following shows the form function used in my website

        <form action="/js/api_sendmessage.js" method="post">
            <div class="row">
                <div class="col-lg-9 form-group">
                    <input type="text" class="form-control" placeholder="Name">
                </div>
            </div>
            <div class="row">
                <div class="col-lg-9 form-group">
                    <input type="email" class="form-control" placeholder="Email address">
                </div>
            </div>
            <div class="row">
                <div class="col-lg-9 form-group">
                    <textarea name="message" id="message" cols="30" rows="7" class="form-control" placeholder="Write your message...">
                </div>
            </div>
            <div class="actions">
                <input type="submit" value="Send Message" />
                <p id="response"></p>
            </div>
            </form>
                

    The website shold be functional with a 'Get in Touch' for to send info to the registered email-id along with a status message for the sender. That brings the end of cloudresumechallenge series of blogs.


    This being my first blog, I'm sure I could have done better. Any suggestion or advise is welcome and thank you for visiting my blog.