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:
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: