# Integrate Your Realm App with Amazon EventBridge

>
>
>This post was developed with the help of AWS.
>
>

Realm makes it easy to develop compelling mobile applications backed by a serverless MongoDB Realm back end and the MongoDB Atlas database service. You can enrich those applications by integrating with AWS's broad ecosystem of services. In this article, we'll show you how to configure Realm and AWS to turn Atlas database changes into Amazon EventBridge events – all without adding a single line of code. Once in EventBridge, you can route events to other services which can act on them.

We'll use an existing mobile chat application (RChat). RChat creates new `ChatMessage` objects which Realm Sync writes to the ChatMessage Atlas collection. Realm also syncs the chat message with all other members of the chat room.

This post details how to add a new feature to the RChat application – forwarding messages to a Slack channel.

We'll add a Realm Trigger that forwards any new `ChatMessage` documents to EventBridge.  EventBridge stores those events in an event bus, and a rule will route it to a Lambda function. The Lambda function will use the Slack SDK (using credentials we'll store in AWS Secrets Manager).

>
>
>Amazon EventBridge is a serverless event bus that makes it easier to connect applications together using data from your applications, integrated software as a service (SaaS) applications, and AWS services. It does so by delivering a stream of real-time data from various event sources. You can set up routing rules to send data to targets like AWS Lambda and build loosely coupled application architectures that react in near-real time to data sources.
>
>

## Prerequisites

If you want to build and run the app for yourself, this is what you'll
need:

-   Mac OS 11+
-   Node.js 12.x+
-   Xcode 12.3+
-   iOS 14.2+ (a real device, or the simulator built into Xcode)
-   git command-line tool
-   realm-cli command-line tool
-   AWS account
-   (Free) MongoDB account
-   Slack account

If you're not interested in running the mobile app (or don't have access to a Mac), the article includes instructions on manually adding a document that will trigger an event being sent to EventBridge.

## Walkthrough

This walkthrough shows you how to:

-   Set Up the RChat Back End Realm App
-   Create a Slack App
-   Receive MongoDB Events in Amazon EventBridge
-   Store Slack Credentials in AWS Secrets Manager
-   Write and Configure the AWS Lambda Function
-   Link the Lambda Function to the MongoDB Partner Event Bus
-   Run the RChat iOS App
-   Test the End-to-End Integration (With or Without the iOS App)

### Set Up the RChat Back End Realm App

If you don't already have a MongoDB cloud account, create one. You'll also create an Atlas organization and project as you work through the wizard. For this walkthrough, you can use the free tier. Stick with the defaults (e.g., "Cluster Name" = "Cluster0") but set the version to MongoDB 4.4.

While your database cluster is starting, select "Project Access" under "Access Manager." Create an API key with "Project Owner" permissions.  Add your current IP address to the access list. Make a note of the API keys; they're needed when using realm-cli.

Wait until the Atlas cluster is running.

From a terminal, import the back end Realm application (substituting in your Atlas project's API keys) using realm-cli:

``` bash
git clone https://github.com/realm/RChat.git
cd RChat/RChat-Realm/RChat
realm-cli login --api-key  --private-api-key 
realm-cli import # Then answer prompts, naming the app "RChat"
```

From the Atlas UI, click on the Realm logo and you will see the RChat app. Open it and make a note of the Realm "App Id":

Optionally, create database indexes by using mongorestore to import the empty database from the `dump` folder.

### Create a Slack App

The Slack app simply allows us to send a message to a Slack channel.

Navigate to the Slack API page. (You'll need to log in or register a new account if you don't have one.)

Click on the button to create a new Slack app, name it "RChat," and select one of your Slack workspaces. (If using your company's account, you may want or need to create a new workspace.)

Give your app a short description and then click "Save Changes."

After creating your Slack app, select the "OAuth & Permissions" link.  Scroll down to "Bot Token Scopes" and add the `chat.write` and `channels:read` scopes.

Click on "Install to Workspace" and then "Allow."

Take a note of the new "Bot User OAuth Access Token."

From your Slack client, create a new channel named "rchat-notifications." Invite your Slack app bot to the channel (i.e., send a message from the channel to "@RChat Messenger" or whatever Slack name you gave to your app):

You now need to find its channel ID from a terminal window (substituting in your Slack OAuth access token):

``` bash
curl --location --request GET 'slack.com/api/conversations.list' \
--header 'Authorization: Bearer xoxb-XXXXXXXXXXXXXXX-XXXXXXXXXXXX-XXXXXXXXXXXXXXXXXXX'
```

In the results, you'll find an entry for your new "rchat-notifications" channel. Take a note of its `id`; it will be stored in AWS Secrets Manager and then used from the Lambda function when calling the Slack SDK:

``` json
{
   "name" : "rchat-notifications",
   "is_pending_ext_shared" : false,
   "is_ext_shared" : false,
   "is_general" : false,
   "is_private" : false,
   "is_member" : false,
   "name_normalized" : "rchat-notifications",
   "is_archived" : false,
   "is_channel" : true,
   "topic" : {
   "last_set" : 0,
   "creator" : "",
   "value" : ""
   },
   "unlinked" : 0,
   "is_org_shared" : false,
   "is_group" : false,
   "shared_team_ids" : 
   "T01JUGHQXXX"
   ],
   "is_shared" : false,
   "is_mpim" : false,
   "is_im" : false,
   "pending_connected_team_ids" : [],
   "purpose" : {
   "last_set" : 1610987122,
   "creator" : "U01K7ET1XXX",
   "value" : "This is for testing the RChat app"
   },
   "creator" : "U01K7ET1XXX",
   "created" : 1610987121,
   "parent_conversation" : null,
   "id" : "C01K1NYXXXX",
   "pending_shared" : [],
   "num_members" : 3,
   "previous_names" : []
}
```

### Receive MongoDB Events in Amazon EventBridge

EventBridge supports MongoDB as a partner event source; this makes it very easy to receive change events from Realm Triggers.

From the [EventBridge console, select "Partner event sources." Search for the "MongoDB" partner and click "Set up":

Take a note of your AWS account ID.

Return to the Realm UI navigate to "Triggers" and click "Add a trigger." Configure the trigger as shown here:

Rather than sticking with the default "Function" event type (which is
Realm Function, not to be confused with Lambda), select "EventBridge,"
add your AWS Account ID from the previous section, and click "Save"
followed by "REVIEW & DEPLOY":

Return to the AWS "Partner event sources" page, select the new source, and click "Associate with event bus":

On the next screen, leave the "Resource-based policy" empty.

Returning to the "Event buses" page, you'll find the new MongoDB partner bus.

### Store Slack Credentials in AWS Secrets Manager

We need a new Lambda function to be invoked on any MongoDB change events added to the event bus. That function will use the Slack API to send messages to our channel. The Lambda function must provide the OAuth token and channel ID to use the Slack SDK. Rather than storing that private information in the function, it's more secure to hold them in AWS Secrets Manager.

Navigate to the Secrets Manager console and click "Store a new secret." Add the values you took a note of when creating the Slack app:

Click through the wizard, and apart from assigning a unique name to the secret (and take a note of it as it's needed when configuring the Lambda function), leave the other fields as they are. Take a note of the ARN for the new secret as it's required when configuring the Lambda function.

### Write and Configure the AWS Lambda Function

From the Lambda console, click "Create Function." Name the function "sendToSlack" and set the runtime to "Node.js 12.x."

After creating the Lambda function, navigate to the "Permissions" tab and click on the "Execution role" role name. On the new page, click on the "Policy name" and then "Edit policy."

Click "Add additional permissions" and select the "Secrets Manager" service:

Select the "ListSecrets" action. This permission allows the Lambda function to see what secrets are available, but not to read our specific Slack secret. To remedy that, click "Add additional permissions" again.  Once more, select the "Secrets Manager" service, but this time select the "Read" access level and specify your secret's ARN in the resources section:

Review and save the new permissions.

Returning to the Lambda function, select the "Configuration" tab and add an environment variable to set the "secretName" to the name you chose when creating the secret (the function will use this to access Secret Manager):

It can take some time for the function to fetch the secret for the first time, so set the timeout to 30 seconds in the "Basic settings" section.

Finally, we can write the actual Lambda function.

From a terminal, bootstrap the function definition:

``` bash
mkdir lambda
cd lambda
npm install '@slack/web-api'
```

In the same lambda directory, create a file called `index.js`:

``` javascript
const {WebClient} = require('@slack/web-api');
const AWS = require('aws-sdk');

const secretName = process.env.secretName;

let slackToken = "";
let channelId = "";
let secretsManager = new AWS.SecretsManager();

const initPromise = new Promise((resolve, reject) => {
   secretsManager.getSecretValue(
      { SecretId: secretName },
      function(err, data) {
            if(err) {
               console.error(`Failed to fetch secrets: ${err}`);
               reject();
            } else {
               const secrets = JSON.parse(data.SecretString);
               slackToken = secrets.slackToken;
               channelId = secrets.channelId;
               resolve()
            }
      }
   )
});

exports.handler = async (event) => {
   await initPromise;
   const client = new WebClient({ token: slackToken });
   const blocks = 
      {
            "type": "section",
            "text": {
               "type": "mrkdwn",
               "text": `*${event.detail.fullDocument.author} said...*\n\n${event.detail.fullDocument.text}`
            },
            "accessory": {
               "type": "image",
               "image_url": "https://cdn.dribbble.com/users/27903/screenshots/4327112/69chat.png?compress=1&resize=800x600",
               "alt_text": "Chat logo"
            }
      },
      {
            "type": "section",
            "text": {
               "type": "mrkdwn",
               "text": `Sent from `
            }
      },
      {
            "type": "divider"
      }
   ]

   await publishMessage(
      channelId, `Sent from RChat: ${event.detail.fullDocument.author} said "${event.detail.fullDocument.text}"`,
      blocks);

   const response = {
      statusCode: 200,
      body: JSON.stringify('Slack message sent')
   };
   return response;

   async function publishMessage(id, text, blocks) {
      try {
            const result = await client.chat.postMessage({
               token: slackToken,
               channel: id,
               text: text,
               blocks: blocks
            });
      }
      catch (error) {
            console.error(error);
      }
   }
};
```

There are a couple of things to call out in that code.

This is how the Slack credentials are fetched from Secret Manager:

``` javascript
const secretName = process.env.secretName;
var MyPromise = new AWS.SecretsManager();
const secret = await MyPromise.getSecretValue({ SecretId: secretName}).promise();
const openSecret = JSON.parse(secret.SecretString);
const slackToken = openSecret.slackToken;
const channelId = openSecret.channelId;
```

`event` is passed in as a parameter, and the function retrieves the original MongoDB document's contents from `event.detail.fullDocument`.

`blocks` is optional, and if omitted, the SDK uses text as the body of the Slack message.

Package up the Lambda function:

``` bash
zip -r ../lambda.zip .
```

From the Lambda console, upload the zip file and then deploy:

!["Upload Lambda Function"

The Lambda function is now complete, and the next section will start routing events from the EventBridge partner message bus to it.

### Link the Lambda Function to the MongoDB Partner Event Bus

The final step to integrate our Realm app with the new Lambda function is to have that function consume the events from the event bus. We do that by adding a new EventBridge rule.

Return to the EventBridge console and click the "Rules" link. Select the "aws.partner/mongodb.com/stitch.trigger/xxx" event bus and click "Create rule."

The "Name" can be anything. You should use an "Event pattern," set "Pre-defined pattern by service," search for "Service partner" "MongoDB," and leave the "Event pattern" as is. This rule matches all bus events linked to our AWS account (i.e., it will cover everything sent from our Realm function):

Select the new Lambda function as the target and click "Create":

### Run the RChat iOS App

After creating the back end Realm app, open the RChat iOS app in Xcode:

``` bash
cd ../../RChat-iOS
open RChat.xcodeproj
```

Navigate to `RChatApp.swift`. Replace `rchat-xxxxx` with your Realm App Id:

Select your target device (a connected iPhone/iPad or one of the built-in simulators) and build and run the app with `⌘r`.

### Test the End-to-End Integration (With or Without the iOS App)

To test a chat app, you need at least two users and two instances of the chat app running.

From Xcode, run (`⌘r`) the RChat app in one simulator, and then again in a second simulator after changing the target device. On each device, register a new user. As one user, create a new chat room (inviting the second user). Send messages to the chat room from either user, and observe that message also appearing in Slack:

#### If You Don't Want to Use the iOS App

Suppose you're not interested in using the iOS app or don't have access to a Mac. In that case, you can take a shortcut by manually adding documents to the `ChatMessage` collection within the `RChat` database.  Do this from the "Collections" tab in the Atlas UI. Click on "INSERT DOCUMENT" and then ensure that you include fields for "author" and "text":

## Summary

This post stepped through how to get your data changes from MongoDB into your AWS ecosystem with no new code needed. Once your EventBridge bus has received the change events, you can route them to one or more services. Here we took a common approach by sending them to a Lambda function which then has the freedom to import external libraries and work with other AWS or external services.

To understand more about the Realm chat app that was the source of the messages, read Building a Mobile Chat App Using Realm – Data Architecture.

## References

-   RChat GitHub repo
-   Building a Mobile Chat App Using Realm – Data Architecture
-   Slack SDK
-   Sending Trigger Events to AWS EventBridge

>
>
>If you have questions, please head to our developer community website where the MongoDB engineers and the MongoDB community will help you build your next big idea with MongoDB.
>
>