HomeGet in touch

How I built my contact form

I used to have a mailto link on this website, which used a combination of SES, Lambda and S3 to forward on any emails to my personal address. This worked fine, and it was a nice way to have an email address on my domain without paying for Google Workspace or something like that. But I ended up with a bunch of spam, so I decided for the new version of the website that I'd add a contact form, using a similar mix of cloud technologies but without having an email address publicly available.

The Lambda script

The bulky part of this is the Lambda script, which is run when a request is received on a route defined in API Gateway.

const { SESClient, SendEmailCommand } = require("@aws-sdk/client-ses");

const client = new SESClient({ region: "us-east-1" });

exports.handler = async (event) => {
  try {
    const { name, email, subject, body } = JSON.parse(event.body);

    const params = {
      Source: "contact@rhysc.me",
      Destination: {
        ToAddresses: ["mypersonalemail@example.com"],
      },
      ReplyToAddresses: [email],
      Message: {
        Subject: {
          Data: subject,
        },
        Body: {
          Text: {
            Data: `${body}\n${name}`,
          },
        },
      },
    };

    const command = new SendEmailCommand(params);

    await client.send(command);

    return {
      statusCode: 200,
    };
  } catch (e) {
    console.error(e);
    return {
      statusCode: 500,
    };
  }
};

Infrastructure

There's a few pieces to this. All of my infrastructure is defined in CloudFormation but it's quite specific to my needs, so I won't share that here. You have to add your domain as an identity in SES, which I don't think you need to verify if you're sending emails to the same address your AWS account is registered with. You can then create your Lambda function, then set up an API Gateway resource to trigger it. There are plenty of guides you can find on Google for each of these bits.