Sending emails with attachments with AWS Lambda and Node.js

The email use case

As a software development company, at XOOR we build websites and web applications for many different purposes. But among all of them, one very common use case is the typical contact form on a company website. It happens that company websites are usually fully static, meaning that we don’t need a web server as we already explained in this post. But whenever you want to add some extra functionality to the website, like a contact form where people can reach out to you, you end up needing some backend code to take care of the email sending.

Choosing an email sending service

First things first. If we don’t have a service that will send emails for us, then we’re in trouble. There are several options around: Mailgun, Mandrill, Mailjet, AWS SES, and many others. Here a good summary of available tools.

AWS SES Configuration

So head over to your AWS accpimt and open the SES management console. The first thing you’ll have to do is to verify an email address which SES will use to send emails from. So under Identity Management, click Email Addresses.

Setting up the IAM Role

Our Lambda function will be executed under a specific IAM role. If you’re not familiar with IAM roles you should read a bit about it on AWS official documentation as it’s the base for all permissions under your AWS account.

  • Access to S3 to send attachments
  • Access to CloudWatch to store execution logs

Setting up the Lambda function

Now it’s time to create our Lambda function. Go to your Lambda management console and click the Create function button. You’ll need to fill in a few fields:

  • Runtime: there are many runtimes available, in our case we’ll be coding in Node.js, so choose Node.js 6.10.
  • Role: Your Lambda function will be executed under a specific IAM role. We’ll choose here the role that was created in the previous section of this article.
The Designer panel on our Lambda function

Lambda Function Signature

Whatever your function does, it must follow a signature known by AWS which is already provided in the Function code panel by default and looks like this:


If you’re too lazy to create a new project and code everything, you can always fork our Github repo and use that as a base.

Setting up the local project

Go ahead and initialize a new node project anywhere in your computer by using
npm init

Choose “Upload a .ZIP file” here and upload the project zip
Use the “Test” button to test the function
Lambda test execution result

Sending a simple email

Let’s put directly the whole code that sends the email here and we’ll review details afterwards. So go ahead and paste the following into your Lambda function in index.js:

  1. We then create a new transporter. Transporters are objects that know how to do certain things depending on the underlying email sending service we use. In our case we’re using AWS SES, and Nodemailer provides already a transporter that understands how to send emails using the AWS SES API. All we have to do is provide a SES client to the transporter and it will use it when needed.
  2. Finally we call the sendEmail function on the transporter and we pass the mailOptions defined at the beginning. If everything goes well, the callback function will be called with no error.

Adding an attachment from S3

In one of our latest projects at XOOR we had to create a function similar to the one we already coded in this post, but it had to send some attachments to the user based on a survey result.

Function to grab a file from S3
Final Lambda function that sends attachments


So we learnt how to send emails with a Lambda function and adding attachments taken from an S3 bucket of our own. This is just a simple first step to implement contact forms or email sending features in fully static websites. On a future post we will see how can we expose this Lambda function through AWS API Gateway, which will allow us to call the function from our static javascript code (Angular, React or whatever you’re using).
We’ll also learn how to protect the API Gateway using CORS headers and how to add validation for the request body.

Don’t miss our posts, follow us now on Twitter! 👇



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