EventBridge (opens new window) is one of the powerful tools in the AWS toolbox when you move towards loosely coupled / event-driven architectures.

In this blog post, I would like to explain by example one of my use cases of EventBridge (opens new window) using API Destinations (opens new window).

# What is EventBridge?

EventBridge (opens new window) is a serverless event bus that enables you to build event-driven applications at scale using events generated from your applications.

# How it works?

Evnebridge connects applications using events, You can fire an event from one of your applications and define filtering rules to route them to specific targets. Those targets can be AWS Services in the same AWS account, another AWS account, or API Destinations (opens new window) via HTTP.

# What are API Destinations?

API Destinations are a form of 3rd party targets(SaaS applications)that can be invoked by event bridge events using REST API calls. API Destinations (opens new window) supports basic, OAuth, and API Key authorization. There are many API destination partners listed here, but you can also use this feature with any 3rd party that has a REST API.

# A working examples

I usually use API Destinations when I want to decouple an asynchronous call from my logic, and when that call invokes a 3rd party REST API.

Let us assume we are implementing the following scenario

GIVEN A User add their details to the website
AND User data is valid
WHEN User data is saved successfully
THEN a UserCreated event is broadcasted
AND an Email is sent to the user
AND an identify fraud check is triggered

So we are building a POST endpoint that accepts user adds a user to the database, and then sends a thank you email to the user after.

We are using SendGrid (opens new window) as an email provider to send the thank you email utilizing their API.

There are multiple ways to achieve that in the cloud to do this asynchronous call but I prefer API Destinations (opens new window) because it is a low code approach. I won't have to write the logic to connect to SendGrid (opens new window) API, manage the retries, etc. I can just send the event and create a rule to call.

# Architecture

The full code explained below can be found in Github (opens new window)

In this example We, we will use the default events bus, and we will use AWS SAM (opens new window) to build our project

# Implementation Steps:

  • Create our lambda functions cloudformation resource

  putUserFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: src/handlers/put-user.putUserHandler
      Runtime: nodejs14.x
      MemorySize: 128
      Timeout: 100
      Description: A simple example includes a HTTP post method to add one user to a DynamoDB table, and send an event to eventbridge
      Policies:
        - DynamoDBCrudPolicy:
            TableName: !Ref UserTable
      Environment:
        Variables:
          USER_TABLE: !Ref UserTable
      Events:
        Api:
          Type: Api
          Properties:
            Path: /user
            Method: POST
  • Add the Arn of the default EventBridge bus, and the SendGrid API so we refer to it in different parts of the template
  CentralBusArn:
    Description: Arn for central event bus
    Type: String
    Default: "arn:aws:events:eu-west-1:0000000000:event-bus/default"
  SendgridApiKey:
    Description: SendGrid API Key
    Type: String
    Default: "Bearer SendGridSecretApiKey"
  • Now lets Write the logic that adds the user to the dynamoDB table
const dynamodb = require('aws-sdk/clients/dynamodb');
const docClient = new dynamodb.DocumentClient();
const { uuid } = require('uuidv4');
const tableName = process.env.USER_TABLE;
exports.putUserHandler = async (event) => {
    const body = JSON.parse(event.body)
    const userData = {id: uuid(), name: body.name, email: body.email}
    var dynamoDBparams = {
        TableName: tableName,
        Item: userData
    };
    // Store the user data in the dynamoDB table
    const result = await docClient.put(dynamoDBparams).promise();
    const response = {
        statusCode: 200,
        body: JSON.stringify(body)
    };
    return response;
}
  • Next we give permission for the lambda to Put Events in EventBridge
      Policies:
        - DynamoDBCrudPolicy:
            TableName: !Ref UserTable
        - Statement:
            - Effect: Allow
              Action:
                - events:PutEvents
              Resource: "*"
  • Next Step lets create our SendGrid destination

  SendgridApiDestination:
    Type: AWS::Events::ApiDestination
    Properties:
      ConnectionArn:
        Fn::GetAtt: [SendgridConnection, Arn]
      InvocationEndpoint: 'https://api.sendgrid.com/v3/mail/send'
      HttpMethod: POST
      InvocationRateLimitPerSecond: 300
  • Now lets create the connection to Sendgrid and refer to the API Key we added previously
  SendgridConnection:
    Type: AWS::Events::Connection
    Properties:
      AuthorizationType: API_KEY
      Description: 'Sendgrid API Credentials'
      AuthParameters:
        ApiKeyAuthParameters:
          ApiKeyName: Authorization
          ApiKeyValue: !Ref SendgridApiKey
  • Now we need to create the filtering rule for the destinations API with the payload we want to send to SendGrid API containing data from the UserCreated event
  ApiDestinationDeliveryRule:
    Type: AWS::Events::Rule
    Properties:
      EventBusName: !Ref CentralBusArn
      EventPattern:
        source:
          - "com.users"
        detail-type:
          - "UserCreated"
      State: ENABLED
      Targets:
        - Arn: !GetAtt SendgridApiDestination.Arn
          Id: SendgridTarget
          RoleArn: !GetAtt SendgridTargetRole.Arn
          InputTransformer:
            InputPathsMap:
              name: $.detail.name
              email: $.detail.email
            InputTemplate: >
              {
                "personalizations": [
                  {
                    "to": [
                      {
                        "email": "hello@example.com"
                      }
                    ]
                  }
                ],
                "from": {
                  "email": "hello@example.com"
                },
                "subject": "<name>, Registration Comlete",
                "content": [
                  {
                    "type": "text/plain",
                    "value": "Hey <name>, Using Eventbridge Destinations is Fun and easy to do ;)"
                  }
                ]
              }
  • Next we create the IAM ole to allow eventbridge to Invoke an API Destination
  SendgridTargetRole:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - events.amazonaws.com
            Action:
              - 'sts:AssumeRole'
      Policies:
        - PolicyName: Amazon_EventBridge_Invoke_Sendgrid_API_Destination
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action: events:InvokeApiDestination
                Resource: !GetAtt SendgridApiDestination.Arn
  • Last thing we modify our code to send the actual event to EventBridge
    const eventBridgeParam = {
        Entries: [
            {
                // Event envelope fields
                Source: 'com.users',
                EventBusName: 'default',
                DetailType: 'UserCreated',
                Time: new Date(),
                // Main event body
                Detail: JSON.stringify(userData)
            },
        ]
    }
    await eventbridge.putEvents(eventBridgeParam).promise()
  • Now to test, all we need to do is to invoke the lambda function by sending a POST request to the API URL
curl -X POST \
  https://bi82zcrsa4.execute-api.eu-west-1.amazonaws.com/Prod/user \
  -H 'cache-control: no-cache' \
  -H 'content-type: application/json' \
  -d '{
	"name" : "Me2resh",
	"email" : "me2resh@example.com"
}'

You should receive the email containing the name of the user from SendGrid

This was one of my favourite use cases of Eventbridge, the full code for the example is here https://github.com/me2resh/eventbridge-api-destinations-example (opens new window). I hope you learned a new way to communicate with an external API through events without writing the code, and only through configuration.