HomeLearnHow-to

Integrate Your Realm App with Amazon EventBridge

Published: Feb 01, 2021

  • Realm
  • Mobile
  • MongoDB
  • ...

By Andrew Morgan

 and Igor Alekseev

Share

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.

Integrate Mobile Realm app with Amazon EventBridge and Slack

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:

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

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:

1git clone https://github.com/realm/RChat.git
2cd RChat/RChat-Realm/RChat
3realm-cli login --api-key <your new public key> --private-api-key <your new private key>
4realm-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":

Integrate Mobile Realm app with Amazon EventBridge and Slack

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):

Invite the RChat Slack app to the channel

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

1curl --location --request GET 'slack.com/api/conversations.list' \
2--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:

1 {
2 "name" : "rchat-notifications",
3 "is_pending_ext_shared" : false,
4 "is_ext_shared" : false,
5 "is_general" : false,
6 "is_private" : false,
7 "is_member" : false,
8 "name_normalized" : "rchat-notifications",
9 "is_archived" : false,
10 "is_channel" : true,
11 "topic" : {
12 "last_set" : 0,
13 "creator" : "",
14 "value" : ""
15 },
16 "unlinked" : 0,
17 "is_org_shared" : false,
18 "is_group" : false,
19 "shared_team_ids" : [
20 "T01JUGHQXXX"
21 ],
22 "is_shared" : false,
23 "is_mpim" : false,
24 "is_im" : false,
25 "pending_connected_team_ids" : [],
26 "purpose" : {
27 "last_set" : 1610987122,
28 "creator" : "U01K7ET1XXX",
29 "value" : "This is for testing the RChat app"
30 },
31 "creator" : "U01K7ET1XXX",
32 "created" : 1610987121,
33 "parent_conversation" : null,
34 "id" : "C01K1NYXXXX",
35 "pending_shared" : [],
36 "num_members" : 3,
37 "previous_names" : []
38 }

#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":

Connect MongoDB as an EventBridge partber source

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:

Create a Realm trigger

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":

Send Realm trigger events to EventBridge

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

Associate partner source with an 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:

Adding a new secret to AWS Secrets Manager

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:

Set AWS Lambda function permissions

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:

Add ARN of secret to Lambda permissions

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):

Add an environment variable for the Lambda function for the name of the secret

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:

1mkdir lambda
2cd lambda
3npm install '@slack/web-api'

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

1const {WebClient} = require('@slack/web-api');
2const AWS = require('aws-sdk');
3
4const secretName = process.env.secretName;
5
6let slackToken = "";
7let channelId = "";
8let secretsManager = new AWS.SecretsManager();
9
10const initPromise = new Promise((resolve, reject) => {
11 secretsManager.getSecretValue(
12 { SecretId: secretName },
13 function(err, data) {
14 if(err) {
15 console.error(`Failed to fetch secrets: ${err}`);
16 reject();
17 } else {
18 const secrets = JSON.parse(data.SecretString);
19 slackToken = secrets.slackToken;
20 channelId = secrets.channelId;
21 resolve()
22 }
23 }
24 )
25});
26
27exports.handler = async (event) => {
28 await initPromise;
29 const client = new WebClient({ token: slackToken });
30 const blocks = [
31 {
32 "type": "section",
33 "text": {
34 "type": "mrkdwn",
35 "text": `*${event.detail.fullDocument.author} said...*\n\n${event.detail.fullDocument.text}`
36 },
37 "accessory": {
38 "type": "image",
39 "image_url": "https://cdn.dribbble.com/users/27903/screenshots/4327112/69chat.png?compress=1&resize=800x600",
40 "alt_text": "Chat logo"
41 }
42 },
43 {
44 "type": "section",
45 "text": {
46 "type": "mrkdwn",
47 "text": `Sent from <https://github.com/realm/RChat|RChat>`
48 }
49 },
50 {
51 "type": "divider"
52 }
53 ]
54
55 await publishMessage(
56 channelId, `Sent from RChat: ${event.detail.fullDocument.author} said "${event.detail.fullDocument.text}"`,
57 blocks);
58
59 const response = {
60 statusCode: 200,
61 body: JSON.stringify('Slack message sent')
62 };
63 return response;
64
65 async function publishMessage(id, text, blocks) {
66 try {
67 const result = await client.chat.postMessage({
68 token: slackToken,
69 channel: id,
70 text: text,
71 blocks: blocks
72 });
73 }
74 catch (error) {
75 console.error(error);
76 }
77 }
78};

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

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

1const secretName = process.env.secretName;
2var MyPromise = new AWS.SecretsManager();
3const secret = await MyPromise.getSecretValue({ SecretId: secretName}).promise();
4const openSecret = JSON.parse(secret.SecretString);
5const slackToken = openSecret.slackToken;
6const 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:

1zip -r ../lambda.zip .

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

Upload the zip file containing the Lambda function and its dependencies

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

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):

Configure EventBridge rule

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

Configure EventBridge rule to route events to the Lambda function

#Run the RChat iOS App

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

1cd ../../RChat-iOS
2open RChat.xcodeproj

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

Set the Realm Application ID in Xcode

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:

GIF showing users sending chat messages using the RChat iOS iPhone app and those messages 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":

Inserting a ChatMessage document into MongoDB Atlas

#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

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.

MongoDB Icon
  • Developer Hub
  • Documentation
  • University
  • Community Forums

© MongoDB, Inc.