How to Integrate AWS Simple Notification Service (SNS) with Twilio SendGrid

November 23, 2022
Written by
Vivek Kumar Maskara
Contributor
Opinions expressed by Twilio contributors are their own
Reviewed by
Diane Phan
Twilion

header - How to Integrate AWS Simple Notification Service (SNS) with Twilio SendGrid

Different services deployed in your infrastructure could emit events that need to be delivered to your end users. Applications deployed on AWS often use Amazon Simple Notification Service (SNS) for inter-service and user communication. Twilio SendGrid provides APIs for delivering an exceptional email experience to your users. You can integrate  SendGrid into your existing workflow to leverage its reliable and scalable email infrastructure.

In this post, learn how to integrate the AWS Simple Notification Service (SNS) with SendGrid to send emails to users. You will learn how to create an AWS Lambda function that subscribes to an SNS topic and sends emails using the SendGrid APIs on receiving a new message. Java language will be used to define the AWS Lambda Handler and the Twilio SendGrid Java library to work with the email APIs.

Prerequisites

For this tutorial, you will need to set up Java on your system since it will be used to define the AWS Lambda handler. You will also need to create an AWS account for deploying the application and a Twilio SendGrid account to send emails. Refer to the list below to set up everything required for this tutorial:

Set up Twilio SendGrid

To use Twilio SendGrid APIs to send emails, you must first create an API key using the SendGrid console. Next, the sender's email ID must be registered and verified before it can be used to send emails.

Create an API key

To create an API key, head over to the Settings in the Twilio SendGrid console and select API keys. Enter the API key name and permissions on the form that appears. For this tutorial, grant Full access to the API key.

Create a new API key using the Twilio SendGrid console

Refer to the docs to understand more about different API permission levels.

Register an email sender

Next, register the email ID that will be used to send emails. SendGrid allows you programmatically send emails using only verified email addresses. You can choose to either verify a single email ID or authenticate a domain. For this tutorial, let us register a single email ID.

To register a sender, head to Settings in the Twilio SendGrid Console and select Sender authentication. Click Verify a single sender on the Sender authentication page to create a new sender.

On the form that appears, enter the sender details and click Create.

Register a new sender using the Twilio SendGrid console

You will receive an email to verify the sender. Once verified, the email ID can be used for programmatically sending emails using Twilio SendGrid APIs.

Examine the system architecture

The diagram below provides an overview of the system, which will help you understand how different components fit together while integrating AWS Simple Notification Service (SNS) with Twilio SendGrid.

System architecture for Twilio SendGrid and AWS SNS integration

A system deployed on AWS could contain multiple applications leveraging AWS EC2, Fargate, Lambda, or Elastic Beanstalk. These applications can potentially publish messages to an AWS SNS topic in response to certain events.

You can define an AWS Lambda function and subscribe it to the SNS topic so that the Lambda is automatically triggered whenever the SNS topic receives a new message.

The Lambda function will call the Twilio SendGrid API to send emails to the end users. All other applications in your AWS stack can continue to publish messages to SNS without re-implementing the email logic.

Let us learn how to define the different components of the system.

Define the AWS Lambda handler

This section describes creating a Java-based AWS Lambda function that will use the Twilio SendGrid Java Library to send emails.

 

If you get lost or you need a reference, the full source code for this tutorial is available on Github.

Create a new Maven Project

To create a new Maven project, open IntelliJ IDE and click on New project. Name the project as "twilio-java-lambda-send-email". Choose Java Language for the project, select Maven for the build system and use JDK version 11.

Create new maven project

Click on Create to create the project. The newly generated project will open in the IntelliJ IDE.

Define a group ID for the Maven project

As per convention, Maven projects use the reversed domain name for group ID. Update your pom.xml file with it with a group ID; this tutorial uses com.maskaravivek as the group ID but feel free to change it to your own preference.

<project
…
    <groupId>com.maskaravivek</groupId>
…
</project>

Create the com/maskaravivek directories inside the src/main/java directory to match the group ID of the project.

Add dependencies in pom.xml

Define the required dependencies in the pom.xml file. To start, add dependencies for the AWS Java SDK and the AWS Lambda Java core libraries. Open pom.xml and add the following definitions inside the <project> tag:

<dependencies>
    <dependency>
        <groupId>software.amazon.lambda</groupId>
        <artifactId>powertools-tracing</artifactId>
        <version>1.12.3</version>
    </dependency>
    <dependency>
        <groupId>software.amazon.lambda</groupId>
        <artifactId>powertools-metrics</artifactId>
        <version>1.12.3</version>
    </dependency>
    <dependency>
        <groupId>com.amazonaws</groupId>
        <artifactId>aws-java-sdk-core</artifactId>
        <version>1.12.296</version>
    </dependency>
    <dependency>
        <groupId>com.amazonaws</groupId>
        <artifactId>aws-lambda-java-core</artifactId>
        <version>1.2.1</version>
        <exclusions>
            <exclusion>
                <artifactId>commons-logging</artifactId>
                <groupId>commons-logging</groupId>
            </exclusion>
        </exclusions>
    </dependency>
</dependencies>

Since the AWS Lambda function will be triggered in response to an AWS SNS message, the function will take in SNSEvent as input. So, add a dependency for the AWS Lambda Java Events library inside the <dependencies> section of the pom.xml file:

<dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-lambda-java-events</artifactId>
    <version>3.11.0</version>
</dependency>

You also need the Gson library for working with JSON. Add a dependency to Jackson since the Twilio SendGrid Java SDK internally parses JSON:

<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.9.0</version>
</dependency>
<dependency>
   <groupId>com.fasterxml.jackson.core</groupId>
   <artifactId>jackson-core</artifactId>
   <version>2.13.4</version>
</dependency>
<dependency>
   <groupId>com.fasterxml.jackson.core</groupId>
   <artifactId>jackson-annotations</artifactId>
   <version>2.13.4</version>
</dependency>

Add a dependency for the Twilio SendGrid Java library:

<dependency>
    <groupId>com.sendgrid</groupId>
    <artifactId>sendgrid-java</artifactId>
    <version>4.9.3</version>
</dependency>

After adding the dependencies, you need to sync Maven dependencies by right-clicking on the project and selecting Maven > Reload project.

Add the build plugin in pom.xml

Add the following build plugin within the <project> section in the pom.xml file in order to package the Maven project and its dependencies into a JAR file.

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>3.3.0</version>
            <configuration>
                <createDependencyReducedPom>false</createDependencyReducedPom>
                <finalName>twilio-java-lambda-send-email</finalName>
            </configuration>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

Define the email request class

The Lambda function receives an SNSEvent which contains one or more SNSRecord objects. Each SNSRecord contains a message of String type. The tutorial uses the message body to send a JSON containing the email's details.

Create a SendEmailRequest.java class in the src/main/java/com/maskaravivek package for the email payload. In IntelliJ IDE, you can create a new Java class by right-clicking on the package name and choosing New > Java Class. Replace the existing contents of the file with the following code:

package com.maskaravivek;

public class SendEmailRequest {
    private String fromEmailId;
    private String toEmailId;
    private String title;
    private String body;

    public SendEmailRequest(String fromEmailId,
                            String toEmailId,
                            String title,
                            String body) {
        this.fromEmailId = fromEmailId;
        this.toEmailId = toEmailId;
        this.title = title;
        this.body = body;
    }

    public String getFromEmailId() {
        return fromEmailId;
    }
    public String getToEmailId() {
        return toEmailId;
    }
    public String getTitle() {
        return title;
    }
    public String getBody() {
        return body;
    }
}

The Lambda handler uses the request class to parse the JSON message from the SNSRecord.

Define the Lambda handler

Next, define the AWS Lambda Handler that receives a SNSEvent as input and uses the Twilio SendGrid APIs to send emails.

Create a TwilioJavaLambdaSendEmailHandler.java class in src/main/java/com/maskaravivek package and replace the existing code with the following:

package com.maskaravivek;

import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.SNSEvent;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.sendgrid.Method;
import com.sendgrid.Request;
import com.sendgrid.Response;
import com.sendgrid.SendGrid;
import com.sendgrid.helpers.mail.Mail;
import com.sendgrid.helpers.mail.objects.Content;
import com.sendgrid.helpers.mail.objects.Email;
import com.amazonaws.services.lambda.runtime.Context;

import java.io.IOException;
import java.util.List;

public class TwilioJavaLambdaSendEmailHandler implements RequestHandler<SNSEvent, Void> {

    Gson gson = new GsonBuilder().setPrettyPrinting().create();

    public Void handleRequest(SNSEvent event, Context context) {
        System.out.println(event);
        List<SNSEvent.SNSRecord> records = event.getRecords();

        for(SNSEvent.SNSRecord record: records) {
            String message = record.getSNS().getMessage();
            SendEmailRequest sendEmailRequest = gson.fromJson(message, SendEmailRequest.class);

            Email from = new Email(sendEmailRequest.getFromEmailId());
            Email to = new Email(sendEmailRequest.getToEmailId());

            Content content = new Content("text/html", sendEmailRequest.getBody());

            Mail mail = new Mail(from, sendEmailRequest.getTitle(), to, content);

            SendGrid sg = new SendGrid(System.getenv("SENDGRID_API_KEY"));
            Request request = new Request();

            request.setMethod(Method.POST);
            request.setEndpoint("mail/send");
            try {
                request.setBody(mail.build());
                Response response = sg.api(request);

                System.out.println(response.getStatusCode());
                System.out.println(response.getHeaders());
                System.out.println(response.getBody());
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }

        return null;
    }
}

The handler iterates over the list of SNSRecord objects and calls the SendGrid email API for each record. The message body is parsed to the SendEmailRequest type and is used to construct the Mail request. Then the SendGrid Request is constructed and sets the Mail object as the request body.

The handler retrieves the values for the SENDGRID_API_KEY from the environment variable. In a later section, you will see how to set environment variables using the AWS console.

Compile the Maven project

Build the Maven project to check if it compiles without errors by clicking on Build > Build Project in the IDE. Alternatively, you can use mvn to compile the application using the following command.

mvn clean compile

After verifying that the project compiles, you can update the version of the Lambda Maven project by modifying the <version> tag from snapshot to a release version in the pom.xml file. 

<version>1.0</version>

Updating to a release version is completely optional, but it is a good practice to use a release version tag for deployments.

Package the Maven project

Next, you need to generate a JAR file for the Maven project. Choose Run > Edit configurations and click on the plus icon in the dialog that appears to add a new configuration. Choose Maven from the menu and set the package as the Run command (or Command line depending on your IDE version) .

Add new build configuration to the maven project

Save and apply the changes to add the new configuration to the project.

Click on Run > Run twilio-java-lambda-send-email [package] to generate a JAR. Once the execution succeeds, a JAR file will be created at target/twilio-java-lambda-send-email.jar.

Deploy the AWS Lambda function

Now, you can deploy the AWS Lambda function you defined in the previous section.

Create a new AWS Lambda function

Head over to the AWS Lambda console and click on Create function.

Create new AWS Lambda function using the AWS console

Enter "TwilioJavaLambdaSendEmailHandler" for the Function name, choose Java 11 (Corretto) as the Runtime environment, and then click Create Function.

Set AWS Lambda function name and runtime language

Upload the Lambda code

On the Lambda function’s page, choose Upload from > .zip or .jar file within the Code source section of the Code tab.

Upload jar file for the AWS Lambda function

Upload the JAR file generated in the previous section. The JAR file will be located within the /target folder of your project's main directory; it should be named twilio-java-lambda-send-email.jar Once uploaded, click Save.

Update runtime settings

Click on Runtime settings > Edit under the Code tab and update the Handler to match the fully qualified handler class in the Maven project: com.maskaravivek.TwilioJavaLambdaSendEmailHandler::handleRequest.  Once copied, click Save.

Update runtime settings for the AWS Lambda function

Add environment variables to the AWS console

Navigate to the Environment variables section under the Configuration tab and click on Edit to add a new environment variable. Add SENDGRID_API_KEY as the Key and set the Value to the API key obtained earlier from the SendGrid console.

Add environment variables to the AWS Lambda function

Take note of the Function ARN located in the Function overview section, as you will need it while creating the SNS topic subscription.

Copy the AWS Lambda function ARN

Create an SNS topic and subscription

In this section, you will create an AWS SNS topic and add an AWS Lambda subscription to it.

Create SNS topic

Go to the AWS SNS console and click on Topics from the left navigation menu. Click on Create topic to create a new SNS topic. Choose Standard as the topic type, enter "TwilioSendEmailTopic" as the Name and click on Create topic.

Create new AWS SNS topic

Create SNS topic subscription

From the SNS topic page, click on Create subscription to add an AWS Lambda subscription. Choose protocol as AWS Lambda and enter the Lambda function ARN that you copied earlier as the Endpoint. Once copied, click Create subscription.

Create AWS SNS topic subscription

Test the SNS subscription

Now that we have all the resources created on AWS, let us publish a test message to the SNS topic to verify if it triggers Lambda, which sends an email.

Head over to the SNS Topic you created earlier by clicking on Topics from the navigation panel on the left and then clicking on your topic name. Then, click on Publish message to create a new message. Copy the following JSON and paste it into the Message body textbox to test the flow.

{
  "toEmailId": "<to_email_id>",
  "fromEmailId": "<registered_sender_email_id>",
  "title": "Hello",
  "body": "Hello world!"
}

Ensure that the fromEmailId matches the one you registered on the Twilio SendGrid console. Once copied you can leave the other fields black, and click on Publish message.

Within seconds, the email ID specified in the toEmailId field should receive the test email through SendGrid.

Test email received using Twilio SendGrid

Conclusion

In this post, you learned how to programmatically integrate AWS SNS to send emails using Twilio SendGrid APIs. This tutorial also demonstrated how to use the Twilio SendGrid Java library in our AWS Lambda function and how to subscribe the function to an AWS SNS topic.

AWS SNS and Twilio SendGrid together make a powerful combination. Your existing applications deployed on various AWS services can continue publishing messages to AWS SNS without worrying about how the event should be communicated to the end users. Twilio SendGrid provides a reliable and scalable infrastructure for sending emails. Combining the two services allows you to build your services faster and deliver a rich user experience.

Vivek Kumar Maskara is a Software Engineer at Remitly.  He loves writing code, developing apps, creating websites, and writing technical blogs about his experiences. His profile and contact information can be found at maskaravivek.com.