Contact Form with AWS SES, Amplify Studio, DynamoDB, and NodeJS Lambda
Tags: aws, lowcode, nextjs, lambda
Published: 2023-07-13 22:30:17



In this tutorial, we will guide you step by step through the process of creating a contact form using `AWS SES` (Simple Email Service), `AWS Amplify`, `NodeJS Lambda`, `DynamoDB`, UI components generated by `Amplify Studio`, and deploying it to a `Next.js` app hosted on GitHub with fully automated deployment. See video [here](https://www.tiktok.com/@albac.dev/video/7254586286583909678?_r=1&_t=8dxw7Ytz88M).

---

## Prerequisites

Before we begin, make sure you have the following prerequisites:
- An `AWS account` with appropriate permissions to create resources
- `AWS CLI` and `Amplify CLI` installed and configured on your development machine
- Basic knowledge of `AWS services`, `Next.js`, and `GitHub`

---

## Step 1: Set up AWS SES

**1**. Login to the AWS Management Console and navigate to the `AWS SES` service.
**2**. Follow the instructions to verify your domain and set up email sending and receiving permissions.
**3**. Once your domain is verified, note down the ARN (Amazon Resource Name) of the verified domain.

---

## Step 2: Host Next.js project with AWS Amplify
**1**. Create a Next.js project by running the following command:
```bash
$ npx create-next-app@latest
```
**2**. Create a GitHub repository for your project, commit and push changes to remote.
**3**. Initialize an Amplify project with the following command:
```bash
$ amplify init
```
**4**. Add hosting with Amplify CLI and select `Continuous deployment (Git-based deployments)` when prompted:
```bash
$ amplify add hosting
```
> Note: There is a more detailed blog explaining this step [here](https://dev.to/albac/host-a-nextjs-blog-with-aws-amplify-and-setup-automated-deployments-6nl)

---

## Step 3: Create Data Model using AWS Amplify Studio
**1**. Launch Amplify Studio by going to the AWS console and selecting `backend environments` under the Amplify service, then selecting the Amplify App we set up in the previous step.
**2**. Once in Amplify Studio, select Data Modeling. Click on `Add model` and enter `ContactForm` as the table name. Select the following field names:
   - Field name: id, Type: ID!
   - Field name: Name, Type: String
   - Field name: Email, Type: AWSEmail
   - Field name: Subject, Type: String
   - Field name: Message, Type: String
**3**. Finally, click on `save and deploy`.

![Contact Data Modeling](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0zq8a982rpp9rd0k9y44.png)

---

## Step 4: Create a Lambda Node.js Trigger

**1**. In the Amplify Studio UI, select `Local setup instructions` on the top right corner and copy the pull command instruction.
**2**. Open a command line under the root of your Next.js project and copy and paste the pull command.

![Amplify Pull](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3kkjqkz3f5dwsbw0yvvv.png)

**3**. After the pull command has been completed, run the `amplify add function` command. When prompted, select the following options:
   - Capability: `Lambda function (serverless function)`
   - Lambda function name: `SendContactInfo`
   - Runtime: `NodeJS`
   - Function template: `Lambda trigger`
   - Event source to associate: `Amazon DynamoDB Stream`
   - DynamoDB event source: `Use API category graphql @model backed DynamoDB table(s)`
   - Advanced setting: `Environment variables configuration` (Add VERIFIED_EMAIL environment key and value)


![Lambda Trigger](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o5utv46bq9eh8cj1q6n0.png)

**4**. After adding the function with the Amplify CLI, you can edit the [`NodeJS lambda source file`](https://github.com/albac/amplify-contactform/blob/main/amplify/backend/function/SendContactInfo/src/index.js).

```javascript
const AWS = require('aws-sdk')
const ses = new AWS.SES

const verified_email = process.env.VERIFIED_EMAIL

exports.handler = async (event) => {

  for (const record of event.Records) {

    if (record.eventName === 'INSERT'){

      const contactName = record.dynamodb.NewImage.Name.S
      const contactEmail = record.dynamodb.NewImage.Email.S
      const contactMessage = record.dynamodb.NewImage.Message.S
      const contactSubject = record.dynamodb.NewImage.Subject.S

      const bodyData = '<br/><b>Contact Name</b>: ' + contactName +
        '<br/><b>Contact Email</b>: ' + contactEmail +
        '<br/><b>Message</b>: ' + contactMessage

      await ses.sendEmail({
        Destination: {
          ToAddresses: [verified_email],
        },
        Source: verified_email,
        Message: {
          Subject: { Data: contactSubject },
          Body: {
            Html: {
              Charset: "UTF-8",
              Data: bodyData
            },
          },
        },
      }).promise()

    }

    return { status: 'email sent'}

  }
  return Promise.resolve('Successfully processed DynamoDB record');
};
```

**5**. Make sure to add the environment variable `verified_email`. You can do this by running `amplify update function` and selecting `Environment variable configuration` under `Available Advance settings`. Alternatively, you can hardcode the value in the following code line:

```javascript
const verified_email = process.env.VERIFIED_EMAIL
```

**6**. Change the [`cloudformation template`](https://github.com/albac/amplify-contactform/blob/main/amplify/backend/function/SendContactInfo/SendContactInfo-cloudformation-template.json#L155-L160) for the lambda to have the permission to use the AWS SES service to be able to send emails.

```json
         "PolicyDocument": {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Action": [
                "ses:SendEmail"
              ],
              "Effect": "Allow",
              "Resource": {
                "Fn::Sub": [
                  "arn:aws:ses:${region}:${account}:identity/albac.dev",
                  {
                    "region": {
                      "Ref": "AWS::Region"
                    },
                    "account": {
                      "Ref": "AWS::AccountId"
                    }
                  }
                ]
              }
            },
```

**7**. To save changes on the cloud, run `amplify push`.

---

## Step 5: Using generated UI Contact by Amplify Studio
**1**. The command `amplify pull` executed in `Step 4` has synced the UI components from Amplify Studio and saved them locally. You can run this command again if necessary.

![Amplify Pull](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3kkjqkz3f5dwsbw0yvvv.png)

**2**. Let's start by creating a [client component](https://github.com/albac/amplify-contactform/blob/main/app/amplifyuitheme.tsx) that will wrap the [Theme provider](https://ui.docs.amplify.aws/react/theming/theme-provider) from Amplify Studio.

```javascript
'use client';

import React, { ReactNode } from 'react';
import { Amplify } from 'aws-amplify';
import awsconfig from '@/src/aws-exports';
import { ThemeProvider, defaultDarkModeOverride } from '@aws-amplify/ui-react';
import '@aws-amplify/ui-react/styles.css';
Amplify.configure(awsconfig);

export default function AmplifyUITheme(props: {children: ReactNode}){

  const theme = {
    name: 'my-theme',
    overrides: [defaultDarkModeOverride],
  };

  return (
    <ThemeProvider theme={theme} colorMode="system" >
      {props.children}
    </ThemeProvider>
  )
}
```

**3**. Then create another [client component](https://github.com/albac/amplify-contactform/blob/main/app/custom

contact.tsx) that will call the `ContactFormCreateForm` generated by Amplify and wrap it with the `AmplifyUITheme` component.

```javascript
...
import ContactFormCreateForm from '@/src/ui-components/ContactFormCreateForm';
import AmplifyUITheme from './amplifyuitheme';
...
   <AmplifyUITheme>
     <ContactFormCreateForm 
       onSuccess={() => setShowModal(false)}
       backgroundColor="neutral.40"
       borderRadius="6px"
       width="40rem"
     />
   </AmplifyUITheme>
```

**4**. Optionally, you can also add a [Modal component](https://github.com/albac/amplify-contactform/blob/main/app/modal.tsx) and use `useState` and `useEffect` to trigger the modal.

```javascript
...
  const [showModal, setShowModal] = useState(false)

  useEffect(() => {
    const onKeyPress = (e: KeyboardEvent) => {
      if (e.key === "Escape") setShowModal(false);
    };

    window.addEventListener("keydown", onKeyPress);
    return () => window.removeEventListener("keydown", onKeyPress);
  },[setShowModal])

  return(
  <>
    <button onClick={() => setShowModal(true)} >
      Contact me
    </button>
    {showModal ? (
      <Modal>
...

```

> Note: For this, we have used one of the best, but at the same time easy to use, modal components by another author. You can check his blog [here](https://codewitholgun.com/how-to-create-a-modal-with-reactjs-and-style-it-with-tailwindcss).

Congratulations! You have successfully created a contact form using AWS SES, AWS Amplify Studio, and AWS Amplify, and deployed it to a Next.js app hosted on GitHub with fully automated deployment.

The final result will look something like this:

![Final Contact](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w8sklfh62jjvut7noauf.png)

----

## Conclusion
In this tutorial, we walked through the process of creating a contact form using `AWS SES`, `AWS Amplify Studio`, and `NodeJS Lambda`. We started by setting up AWS SES and hosting a Next.js project with AWS Amplify. Then, we created a data model using AWS Amplify Studio and set up a Lambda Node.js trigger to handle the form submissions and send emails using AWS SES.

We also utilized the `UI components` generated by `Amplify Studio` to create a user-friendly contact form interface. Finally, we deployed the `Next.js app` to `GitHub` with automated deployment.

Feel free to customize and enhance the contact form and the overall application according to your specific requirements. Happy coding!

Happy coding!

---

## References
* **Video**: [https://www.tiktok.com/@albac.dev/video/7254586286583909678?_r=1&_t=8dxw7Ytz88M](https://www.tiktok.com/@albac.dev/video/7254586286583909678?_r=1&_t=8dxw7Ytz88M)
* **GitHub Code**: [https://github.com/albac/amplify-contactform/](https://github.com/albac/amplify-contactform/)
* **Amplify UI Theme Provider**: [https://ui.docs.amplify.aws/react/theming/theme-provider](https://ui.docs.amplify.aws/react/theming/theme-provider)
* **Modal Blog Component**: [https://codewitholgun.com/how-to-create-a-modal-with-reactjs-and-style-it-with-tailwindcss](https://codewitholgun.com/how-to-create-a-modal-with-reactjs-and-style-it-with-tailwindcss)

---