Send Files with API Gateway & SES

Using API Gateway with SES to send Emails with Attachments for a simple contact form. (Amplify Framework/React JS)

AWS Amplify simplifies the manual setup for our backend. Having created a React project as well as configuring it with the Amplify Console we can begin setting up our serverless backend.

Getting Started

Make sure you have the Amplify CLI npm package

npm install -g @aws-amplify/cli

Install all Amplify modules or just the API Gateway module.

npm i aws-amplify or npm i @aws-amplify/api

Now we begin installing our backend API which creates a lambda function to perform our serverless operations.

amplify add api

…make sure you create a lambda function with an Express template when prompted, this integrates API Gateway with Lambda so you can make REST calls (POST/GET/PUT etc.) which is computed using your preferred runtime, in this case NodeJS.

Once you have created your API and Lambda you can now install packages to them. We will be using two packages: NodeMailer and express-fileupload as well as aws-sdk.

cd amplify/backend/function/<function name>/srcnpm i nodemailer express-fileupload aws-sdk

Nodemailer handles the headers for sending a raw email, raw emails are the only way to attach files to emails), where express-fileupload will parse our files from our REST calls.

Front-End

For our front end we will be using a few packages including a UI-Framework.

npm i formik yup
npm i @material-ui/core

Formik is a popular form package for simplifying the form-state logic, as well as Yup which integrates nicely with formik for validation schema.

And of course we are including my favorite UI-Framework Material-UI, This makes use of a clean UX/UI for our front-end.

Before I jump to our App.js we need to include a utils.js to our src code.

utils.js

This gives us the ability to view the size of the files as well as convert the files to base64 encoding for transmission over the REST API.

App.js

If you are not interested in the UI-Framework, then pay attention to the <input type=’file’ …/> (row:133) and the submission function (row:46). Also Notice that we convert the FileList to array (row:136), this allows us to utilize the value Files as an array.

Notice we use:

const formData = new FormData();

This is different than just sending the object as is. FormData allows us to attach files however since FormData encodes data in ASCII (7-bit), data is unreadable on the backend. To prevent this we encode in base64 before transmission to avoid this.

Don’t get intimidated by the reduce function that is adding files to the FormData object. You can read here how it works, but this allows us to sequentially iterate an async/await loop instead of simultaneously (parallel) iterate through the loop.

Also note our new Blob accepts a string array. Be sure to include file type and name.

And that’s it. You can send your data via the API.post function (or prefered request function such as axios, fetch, etc.) by including it in the body key of the options parameter. Make sure you use the correct API name and path you plan to use.

Note both API Gateway and SES have both a limit of 10 MB in size.

This is the form which can include multiple files. You can exchange the basic upload with something more advanced such as react-dropzone which allows you to drag and drop files, as well as store them in states.

Back-End

For our backend we edit our lambda function, assuming you have installed the packaged mentioned at the beginning of our article we can begin implementing our Express backend.

Be sure to have verified your sending email and domain, (e.g. no-reply@domain.com) as for receiving you may either send to your personal email or also set that up with Amazon SES or Amazon WorkMail (e.g. pr@domain.com). If you are not using Route53 for your domain hosting then you should change your DNS settings with your current DNS provider.

Also very important, add the IAM policy ‘SES Full Access’ (or similar) to your lambda function role. This gives your lambda function permission to send emails via SES.

app.js

Make sure to include app.use(fileUpload()); This allows the backend to separate the fields from the files. Note we use us-region-1 since that has the ability to receive email whereas us-region-2 cannot.
amplify push 

There you have it, test your app and you should see a response on your front-end. Check your emails to see if you received your message.

Thank you for reading, hope you enjoyed.

M.S. Electrical Engineer, Working as a Software Developer mainly with AWS.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store