UK based website and application support - contact form.

Blog.

Connect React form to Amazon SES

Cover Image for Connect React form to Amazon SES

Recently I've decided to go fully static with my blog. Years ago I've created simple node.js script to send emails via Sendgrid service. I since moved on to AWS so I thought it's time to switch email service to Amazon SES - especially after reading this great blog post https://www.smashingmagazine.com/2018/05/building-serverless-contact-form-static-website/.

FormFormAmazon Lambda function that calls an APIAmazon Lambda...Send an e-mailSend an e-mailcallback with status code 200 or 500callback with status...Post form data to an endpointPost form data to an...call handlercall handlerhandler calls SES.sendEmail() with params, callback andCORS header to form hosthandler calls SES.sendE...Amazon SESAmazon...Amazon API GatewayAmazon API Ga...Viewer does not support full SVG 1.1

React contact form

Firstly create simple react component with form:

// contact-form.js

class ContactForm extends React.Component {
  name = React.createRef()
  emailAddress = React.createRef()
  message = React.createRef()

  formSubmit = e => {
    // Form on submit functionality
  }

  render() {
    return (
      <>
        <h4>Contact form</h4>
        <form onSubmit={this.formSubmit}>
          <label for="name">
            Name
          </label>
          <input type="text" name="name" ref={this.name} required />
          <label for="email-address">
            Email
          </label>
          <input type="email" name="email_address" ref={this.emailAddress} required />
          <label for="message">
            Message:
          </label>
          <textarea name="message" ref={this.message} required></textarea>
          <button type="submit">
            Send Message
          </button>
        </form>
        <p id="form-response"></p>
      </>
    )
  }
}

export default ContactForm

To add component to your code import it import ContactForm from './contact-form' or using CommonJS require var ContactForm = require('.contact-form') and simply use <ContactForm> in your code. This script will create React component that looks like this:

React form component

Amazon SES (Simple Email Service)

With form ready it is time to setup Amazon SES. For that I referr back to original article: Building A Serverless Contact Form For Your Static Site with following tweaks:

1. Call local SES service

As I'm UK based I want to use SES service using London servers. To do that call SES with region parameter:

const SES = new AWS.SES({ region: 'eu-west-2' });

More about Amazon's Simple Email Service (SES) https://aws.amazon.com/ses/

2. Use axios() to send data to API gateway

Axios is well tested, widely used promise based HTTP client. Fetch API can be used as an alternative solution but make sure to add a polyfill as fetch is not supported on IE11, in fact any IE :(

Makig API calls from React

It's time to create script that will call API gateway which will trigger a call to Amazon SES.

  formSubmit = e => {
    const ENDPOINT = "https://UNIQUE_STRING.execute-api.us-east-1.amazonaws.com/dev/static-site-mailer"
    e.preventDefault()

    const name = this.name.current.value
    const email = this.emailAddress.current.value
    const message = this.message.current.value

    if (typeof name === 'string' && typeof email === 'string' && typeof message === 'string') {
      const body = {
        to: "[email protected]",
        reply_to: email,
        name: name,
        message: message
      }

      postData(ENDPOINT, body)
        .then(response => console.log("Success:", JSON.stringify(response)))
        .catch(error => console.error("Error:", error))
    } else {
      console.warn("Data error...")
    }
  }

As you can see formSubmit purpuse is to take form data and make a call to postData which will send POST request to API. Make sure body object is correct before passing it to postData.

async function postData(url, body = {}) {
  const config = {
    headers: {
      'Accept': 'application/json; charset=utf-8',
      'Content-Type': 'application/json; charset=UTF-8'
    },
  };

  const response = await axios({
    method: 'post',
    url: url,
    body: JSON.stringify(body),
  }, config);

  return response.status;
}

postData will make a POST call to endpoint and pass form data in body element. I've used async/await in my callback but .then().catch() syntax is faster, so use it on high traffic webapps. I highly recomment to set Maximum daily sends to limit daily e-mail sends. Maximum sending rate allows to limit sends per second. Both can be adjusted via AWS Console.

Troubleshooting

Can't find API gateway in AWS console?

Check if AWS region is set correctly. AWS region will default to us-east-1 when using serverless. If you wish to change it add region field to serverless.yml. For example to set it to London add:

// serverless.yml

region: eu-west-2

Use cUrl to send test CORS request:

When testing form you might notice CORS error in browser similar to:

[Error] Origin 'https://example.com/my-form is not allowed by Access-Control-Allow-Origin'.
[Error] Fetch API cannot load 'https://example.com/my-form due to access control checks'.
[Error] Failed to load resource: Origin 'https://example.com/my-form is not allowed by Access-Control-Allow-Origin. (my-app, line 0)'

Use cUrl console command to test endpoint response and possible CORS error. Example command:

curl -H "Origin: http://example.com" --verbose \
  https://my-endpoint-example.com

Still struggling with CORS? Here is a great resource about CORS on MDN: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

Setup wildcard CORS domain

Simply use * instead of domain name. This way API should accept calls from any domain including localhost. Unfortunatelly Access-Control-Allow-Origin only accepts single value or null, so it is not trivial to add multiple domains (it is not impossible, as always :))

'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',

TypeError: undefined is not an object (evaluating 'e.json')

Make sure all parameters passed to SES.sendEmail() are correct. Check amazon docs [here] (https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/SES.html#sendEmail-property).

Check Cloudwatch Logs for errors

Sometimes console.log might give very brief error. Best way is to verify Cloudwatch logs. For example it might be that field is mistyped:

Clourwatch Log

← Back to homepage

Looking for website and application support in the Midlands? I specialize in providing comprehensive website maintenance and support services.
Whether you need hosting solutions, web application development, security updates, or assistance with in-house or remote teams, I can help.

My expertise covers React, Next.js, JavaScript, TypeScript, Web Components, Lit, Stencil.js, Node.js, RESTful APIs, Docker, Kubernetes, and Amazon Web Services.

Get in touch!