How to Build a Serverless Contact Form on AWS: SES, Lambda & API Gateway

As a full-stack developer, you may be tasked with adding a contact form to a website that sends emails directly to the business owner or team. While you could use a ready-made solution like Formspree or Wufoo, rolling your own with AWS offers several benefits:

  • Highly scalable and cost-effective, as you only pay for the resources you consume
  • Customizable to fit the website‘s design and data requirements
  • Provides full control and ownership over the form data and submission process
  • Opportunities to learn and leverage powerful AWS services

In this guide, we‘ll walk through building a serverless contact form that uses AWS Simple Email Service (SES) to send emails, AWS Lambda to process form submissions, and AWS API Gateway to create a public endpoint. By the end, you‘ll have a working example that you can adapt to your own projects.

Why Serverless?

Serverless architectures have grown in popularity for good reason. They allow developers to focus on writing code without worrying about provisioning or managing servers.

For a contact form, the serverless approach is ideal because:

  • Form submissions are typically infrequent, making a dedicated server wasteful
  • Traffic can be unpredictable, requiring the backend to scale up and down automatically
  • Processing and sending emails is a short-lived, independent task well-suited for Function-as-a-Service (FaaS)

Using AWS Lambda, you can run code on-demand in response to form submissions and only incur charges for the computing time actually used (100ms increments). This is much cheaper than running a server 24/7 just to handle a few form submissions per day.

Here‘s a visualization of the serverless contact form architecture we‘ll be implementing:

[Architecture Diagram: HTML Form -> API Gateway -> Lambda Function -> SES -> User‘s Email Client]

Choosing AWS Services

While there are many ways to build a contact form, using AWS services offers several advantages:

  • SES provides a reliable, scalable way to send emails from your app without having to run your own email server. It‘s cost-effective, with a generous free tier and low per-email pricing.

  • Lambda lets you run code without provisioning infrastructure, and easily integrates with other AWS services. It supports popular languages like Node.js, Python, and Java.

  • API Gateway makes it simple to create a public HTTPS endpoint that can securely receive form submissions from the browser and trigger your Lambda function.

Of course, there are tradeoffs to consider. Some challenges of serverless include:

  • Cold starts: Initial requests may have higher latency as the Lambda function is loaded and instantiated.
  • Limited execution time: Lambda functions can only run for up to 15 minutes. Not an issue for emails, but could be limiting for some use cases.
  • Vendor lock-in: Code is closely tied to AWS services and APIs, making it harder to switch cloud providers later.

For most basic contact forms, the benefits of serverless outweigh the drawbacks. But it‘s important to evaluate your specific needs.

Tutorial Steps

Now let‘s get into the step-by-step process of creating the serverless contact form on AWS.

1. Create the HTML Form

Start by adding a simple form to your website‘s HTML:

<form id="contact-form">
  <label for="name">Name:</label>
  <input type="text" name="name" required>

  <label for="email">Email:</label>
  <input type="email" name="email" required>

  <label for="message">Message:</label>
  <textarea name="message" required></textarea>

  <button type="submit">Send</button>
</form>

<div id="result-text"></div>

We‘ve included fields for name, email, and message – adjust these to fit your needs. The result-text div will display a success/error message after submission.

2. Verify an Email Address in SES

To send emails from SES, you first need to verify the sender email address:

  1. Open the AWS SES console
  2. Click "Verify a New Email Address" under Identity Management
  3. Enter the email you want to use as the sender and click "Verify This Email Address"
  4. Check your inbox and click the verification link

SES requires verification to confirm you own the emailing address and have authorized it to send on your behalf.

3. Create an IAM Role for Lambda

Your Lambda function will need permission to send emails through SES. The best practice is to assign these permissions using an IAM role:

  1. Open the AWS IAM console
  2. Click "Roles" and "Create Role"
  3. Choose "AWS service" and select "Lambda" as the use case, then click "Next"
  4. Search for and select the AmazonSESFullAccess policy, then click "Next"
  5. Name the role (e.g. lambda-ses-role) and click "Create role"

This role will allow your Lambda function to call the SES API to send emails.

4. Create the Lambda Function

Now let‘s implement the function that will process the form data and send the email:

  1. In the AWS Lambda console, click "Create function"
  2. Name your function (e.g. contact-form-mailer) and select Node.js as the runtime
  3. Under Permissions, choose "Use an existing role" and select the IAM role you created
  4. Click "Create function" then paste the following code in the editor:
const aws = require(‘aws-sdk‘);
const ses = new aws.SES({region: ‘us-east-1‘});

exports.handler = async (event) => {
  const formData = JSON.parse(event.body);

  const emailParams = {
    Source: ‘[email protected]‘,
    Destination: {
      ToAddresses: [‘[email protected]‘]  
    },
    Message: {
      Body: {
        Text: {
          Charset: ‘UTF-8‘,
          Data: `
            Name: ${formData.name}
            Email: ${formData.email}
            Message: ${formData.message}
          `
        }
      },
      Subject: {
        Charset: ‘UTF-8‘,
        Data: ‘New contact form submission‘
      }
    }
  };

  try {
    await ses.sendEmail(emailParams).promise();
    return {statusCode: 200, body: ‘Message sent!‘};
  } catch(err) {
    return {statusCode: 500, body: ‘Error: ‘ + err};
  }
};

This function extracts the form fields from the HTTP request body, constructs an SES sendEmail API request with the field values, and returns a response indicating success or error.

Make sure to update the Source and Destination email addresses. You can also customize the email subject and body template.

5. Configure API Gateway

To expose your Lambda function as a web service, create an API Gateway endpoint:

  1. In the API Gateway console, click "Create API"
  2. Choose "REST API" and "New API", give it a name, then click "Create API"
  3. Under the "Actions" dropdown, choose "Create Method" and select "POST"
  4. For Integration type, choose "Lambda Function" and select your function
  5. Click "OK", then under "Actions" choose "Deploy API"
  6. Select "New Stage", name it (e.g. "prod"), and click "Deploy"
  7. Take note of the "Invoke URL" as you‘ll need it in the next step

Your API Gateway endpoint is now live and ready to accept form submissions!

6. Handle the Form Submission with JavaScript

Finally, add a JavaScript function to your website that triggers on form submit, validates input, and makes an AJAX request to your API Gateway:

const form = document.getElementById(‘contact-form‘);
const resultText = document.getElementById(‘result-text‘);

form.addEventListener(‘submit‘, async (e) => {
  e.preventDefault();
  resultText.textContent = ‘‘;

  const formData = new FormData(form);
  const jsonObject = {};
  for (const [key, value] of formData.entries()) {
    jsonObject[key] = value;
  }

  try {
    const response = await fetch(‘https://YOUR_API_GATEWAY_URL‘, {
      method: ‘post‘,
      body: JSON.stringify(jsonObject)
    });
    const data = await response.json();
    if (!response.ok) throw Error(data.body);
    resultText.textContent = data.body;
    form.reset();
  } catch (err) {
    resultText.textContent = err;
  }
});

This code intercepts the form submission, serializes the form data as JSON, and sends it to your API Gateway URL using fetch. If successful, it displays the "Message sent!" response and resets the form. On error, it shows the error message.

Be sure to replace YOUR_API_GATEWAY_URL with the actual Invoke URL from the previous step.

Improving Security & Reliability

While the basic serverless contact form is now functional, there are some enhancements worth considering for a production deployment:

  • Input validation and sanitization: Check for malicious or suspicious input on both the frontend (JavaScript) and backend (Lambda). Use a library like validator.js or DOMPurify.

  • Captcha: Add a captcha like reCAPTCHA to the form to prevent automated spam submissions. You‘ll need to validate the captcha response in your Lambda function.

  • Email content filtering: Scan the email content for spam, viruses, or other unwanted content before sending. AWS provides a machine learning-based content filter for this purpose.

  • Rate limiting: Implement rate limiting in API Gateway to prevent abuse and protect your Lambda function from being overwhelmed by too many requests.

  • Logging and monitoring: Use AWS CloudWatch to log and monitor your Lambda function‘s execution and set up alarms to notify you of failures or anomalies.

  • Error handling: Implement graceful error handling in your Lambda function and provide meaningful error messages to the user. Use Amazon SNS to send notifications of critical errors.

Conclusion

Serverless architectures provide a powerful yet cost-effective way to handle website contact forms. By leveraging AWS services like SES, Lambda, and API Gateway, you can build a robust solution that scales effortlessly to handle traffic spikes without breaking the bank.

More importantly, this approach lets you focus on your core product without getting bogged down in server management or email deliverability. You can further customize and extend the serverless contact form to fit your specific needs, such as saving form submissions to a database or sending auto-responses.

While there is a learning curve to working with AWS and serverless, the payoff is worth it. You‘ll gain valuable skills and be able to deliver more value to your clients or company. So give it a try and see what you can build!

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *