This is the second blog in a five-part series.

Building a Slack app that integrates with Salesforce involves some challenges, such as knowing the right integration capability to use on each case, picking the right authorization flow, and implementing it securely. This is the second blog post of a series in which we cover the whole process of creating a Slack app that integrates with Salesforce from scratch. Learn along with us while we build Ready to Fly, our new sample app for submitting and approving travel requests in Salesforce without leaving Slack.

Note: some parts of this app have been live coded by my colleagues Mohith Shrivastava (@msrivastav13) and Kevin Poorman (@codefriar) in their codeLive video series: Building with Slack.

Check out the rest of the blogs in this series here:

The Bolt framework

Let’s continue our series by talking about the Bolt framework, which provides a web server to run your app on, sets up authentication with the Slack app, and gives you simplified interfaces to work with Slack APIs and features. The Bolt framework is available for Python, JavaScript, and Java. Per our familiarity with Lightning Web Components (LWC), we decided to use the JavaScript version and build a Node.js app for Ready to Fly.
Focus areas of this blog post are highlighted in red

How Slack apps work

If you’re familiar with web development, you’ll know that elements on a web page can react to events like “click” or “hover.” In a similar way, events occur when users perform actions in the Slack UI. For example, Slack fires events when messages are posted to a channel and when an app home is opened.

In JavaScript, you react to events by defining listeners, and it’s the same in Bolt.js. You can define listeners for events, actions, shortcuts, views, messages, and options — really different types of events. You generally write some logic in the listener, do a callout to an external system if needed, and then send changes to Slack that need to be reflected in your app’s UI.

Slack UIs are defined in JSON with a framework called Block Kit. You send a list of blocks over to a Slack API, and Slack generates the layout for you in a consistent manner. This minimizes the amount of work you have to do to create a layout, and it guarantees that all Slack apps have a consistent UX foundation. Bear in mind, Block Kit is the only way to define UIs; injecting HTML is not allowed.

Remember the app home of our Ready to Fly app?
Ready to Fly app home

Let’s explore the steps that are needed to render the user’s travel requests:

  1. The user opens the app home. The Events API publishes the app_home_opened event to your event subscription URL. You’ll need to set up this URL in the Slack app definition (more about this later).
  2. If the user is authorized with Salesforce, the Node.js app requests the user’s travel requests from Salesforce, using JSforce, a JavaScript library for use with the Salesforce APIs. You’ll need to create a connected app in Salesforce to handle authentication.
  3. Salesforce responds by sending the user’s travel requests.
  4. The Node.js app generates the UI with Block Kit, and publishes it to the app home.

A flow that shows travel requests when a user visits the app home

Let’s see another example. In Ready to Fly, an approver can see a travel request and then either approve or reject it.

Buttons that allow approvers to interact with travel requests

Let’s see how this flow works:

  1. The user clicks the “Approve” or “Reject“ buttons. Slack notifies the Node.js app that the button has been pressed, sending a block_actions payload to your interactivity URL. This is another URL that you’ll need to set up in the Slack app definition.
  2. If the user is authorized with Salesforce, the Node.js app sends the travel request status update to Salesforce, using JSforce.
  3. The Node.js app generates a modal UI with Block Kit and posts it to Slack. Slack prompts the modal to the user.

A flow that shows approval or rejection of travel requests after a user clicks on a button

Creating the Slack app

The first step to creating a Slack app is to define its configuration at api.slack.com. This is done in a UI. You’ll need to log into your Slack workspace and then indicate your preferences for the app, such as which features do you want to activate, which events do you want the app to publish, when they occur, etc. You can configure all of this declaratively in the UI or by copying a manifest.yml file, which is very convenient and fast. For instance, this is the manifest of the Ready to Fly app that I created in my test workspace:
The app manifest for the Ready to Fly Slack app

Notice that the Heroku app URL is set up in the event_subscriptions request_url property of the Slack app manifest as Slack needs to know which endpoint to notify when events happen.

The Heroku app URL must also be set up in the interactivity request_url property, so that Slack notifies Heroku when users interact with shortcuts, modals, or interactive components (actions).

Node.js app structure

Let’s take a look at the Node.js app structure and talk about its various folders. This will help you to navigate the app files in the examples shown in the following sections.

The file structure of a Node.js app

  • listeners: where we define the listeners for events, actions, shortcuts, and views
  • middleware: where we store the global middleware that we use for authentication (more about this in the next blog post)
  • routes: where we create custom HTTP routes, such as the one we create so that Salesforce can post messages when the status of travel requests change
  • salesforce: where we store the code to interact with Salesforce, in our case querying and performing DML operations
  • store: where we created some classes to persist some data and make it available for custom HTTP routes
  • user-interface: where we store the code that creates all the user interface bits in the app
  • app.js: entry point for the app, where all the listeners, custom HTTP routes, and middleware are registered

Defining listeners

When using Bolt.js, the Slack event listener is a callback that you need to register in the app:

Let’s explore different types of listeners that you can create:

Events
Events occur in Slack when users perform standard actions in the Slack UI. In Bolt.js, you listen to events using the app.event() function. For instance, here’s how we listen to the app home opened event in Ready to Fly:

The app_home_opened event is fired when users clicks on the app’s Home tab. Actions
When users engage with interactive UI components, such as a menu or a button, Slack fires actions (a special type of event). Note that the name of the action needs to be attached to the interactive element when it’s created.

The authorize-with-salesforce custom action is fired when this button is clicked.

In Bolt.js, you listen to actions using the app.action() function. In Ready to Fly, we listen to the actions that fire when the user clicks on the buttons of our app:

Shortcuts
In Slack, you can also define shortcuts, a special entry point that can be global for the app or message scoped. Shortcuts are defined in the configuration of the Slack app, and you can use the manifest for that. In Ready to Fly, we created a global shortcut to open a modal that allows you to create travel requests.

The create_travel_request shortcut defined in the app manifest

The create_travel_request shortcut is fired when the user clicks on it.

Just like for events and actions, you can listen to shortcuts. In Bolt.js, you listen to shortcuts using the app.shortcut() function. This is the code that listens to the create_travel_request shortcut in Ready to Fly:

Views
When the view_submission or view_closed events occur, a view listener fires. This happens when the user submits a view or dismisses a modal. In Ready to Fly, we created a modal that allows users to create travel requests, and we used a view listener to execute logic when the modal is submitted. Note that the name of the view callback needs to be attached to the modal when it’s created.

The initiate_travel_request view is fired when the user submits the modal.

In Bolt.js, you listen to view submissions using app.view(). This is the code that listens to the submission of the initiate_travel_request modal in Ready to Fly:

There are additional listeners that we haven’t used in Ready to Fly, such as messages, commands (as the use of shortcuts is preferred), and options. These concepts are very similar, but be sure to take a look at the documentation if you are interested in knowing more. Also, take a look at the parameters that each kind of listener receives in the reference (see the “Listener function arguments” section).

Creating user interfaces with Block Kit

To help you create Slack UIs with Block Kit, there is a tool called Block Kit Builder that will help you mock up what you want to build in advance. Indeed, you can send fancy messages to Slack directly from Block Kit Builder — nothing related to your app development, but still a useful tool! The Block Kit Builder

In Bolt.js, listeners always receive a client parameter that you can use to call the Web API methods. This way, you can easily create or modify your Slack app UIs, for instance publishing a view to the app home, opening or updating views in modals , or sending messages. Most Web API methods accept a UI that’s built with Block Kit.

For instance, in Ready To Fly, we could render some test travel requests in the app home as follows:

Executing that code, we would get this result:
A UI built with Block Kit and published in the app home

However, defining everything in JSON can be tedious and error prone. That’s why we decided to go a step further and use a library for that: Block Builder. Block Builder is an open source library built by the Slack community that uses a declarative and chainable syntax to keep the JSON creation code maintainable, testable, and reusable. Using Block Builder, the code above would be equivalent to:

Much easier to read and maintain, right? I really enjoyed using this library. If you want to know more about Block Kit, I recommend that you to tackle this Trailhead module.

Defining custom HTTP routes

Custom HTTP routes provide an entry point to the Node.js app that you can call out from an external system. In other words, this allows you to expose your own HTTP API for interacting with your app. A custom HTTP route is really a standard Express router, where Express is a is lightweight HTTP server library for Node.js.

In Ready to Fly, we use a custom HTTP route to send messages from Salesforce to the Node.js app. We could have used Webhooks to post messages in the Slack app directly, but that didn’t work for our use case because we needed to execute some logic before posting the messages to Slack.

Let’s take a look at a flow in which the aforementioned custom HTTP route is involved:

  1. When travel request’s status changes, Salesforce makes an HTTP callout to the Node.js app. We store the Heroku app URL in a custom metadata record that’s deployed with the deploy script.
  2. The Node.js app checks that the message’s target user is authorized, builds the message with Block Builder, and sends it to Slack.

Salesforce sends messages to a custom HTTP route that in the end are posted to Slack.

Note that to keep track of user sessions during the Salesforce OAuth flow dance, we took advantage of express-session (see docs), which is an npm module to manage sessions in the server. Using express-session forced us to use a custom ExpressReceiver. The way in which custom HTTP routes are registered in this case is slightly different from the regular one, but it’s all documented.

Wrapping up

That’s all for this blog post. I hope that you now have a good understanding of how Slack apps work, how to use Bolt.js to interact with them, and how to build Slack UIs with Block Kit. If you want to know more about this topic, check out our Slack for Salesforce Developers blog post.

Stay tuned — in the next post of the series, we’ll continue talking about Salesforce integration.

About the author

Alba Rivas

Alba Rivas works as a Principal Developer Advocate at Salesforce. She focuses on Lightning Web Components and Lightning adoption strategy. You can follow her on Twitter @AlbaSFDC.

Get the latest Salesforce Developer blog posts and podcast episodes via Slack or RSS.

Add to Slack Subscribe to RSS