August 14, 2019

Building a Slack App using 8base functions

8base
@8base

Slack is an excellent application for in-house communication between teams. Its extensibility means we can easily extend Slack by building and integrating chatbots and slash commands that enable users to communicate with external services. A great Slack bot that is widely used and very helpful is the GitHub bot that posts updates on pull requests, commits and issues in a channel.


In this article, we will build a Slack bot that posts updates when an order is created on a make-believe e-commerce platform that we run. When an order is created on the e-commerce platform, an update is posted to the channel.

The e-commerce site is a serverless application built using 8base. Feel free to follow along in this article as we create the application. In this article, we will use 8base functions to listen for Create events on a data model using Triggers.

A trigger is a type of function that runs in response to a data mutation event: creating, updating or deleting an object. 8base exposes two types of triggers based on whether the developer wants the function to run before or after the data is modified: trigger.before and trigger.after.

Requirements

  • A basic understanding of React and Node.js is required. Please ensure that you have Node and npm/yarn installed before you begin.
  • We'll also be making use of some GraphQL queries in the project, so some familiarity with GraphQL is helpful.
  • Create an 8base account if you haven't already by visiting our website.

Preparing the 8base environment

  1. Create an account or login on 8base. For new accounts, 8base gives you a free 30-day trial plan.

  1. Once logged in, navigate to the data page and click on "New Table" to start building your backend.

  1. After your new table is loaded, you'll be shown the schema and able to begin defining fields. Go ahead and create a table called Orders that consists of the following fields:
  2. user: Set the field type to Table.  In the section on the right, search for the Users table and add the relation name as order. Finally, tick the “Allow multiple Orders per User” checkbox.


  • items: field type is JSON. This field holds information about the items the user is purchasing, like the price, name, and image.

Our schema is quite simple and should cover the features that we're trying to accomplish. Next, lets set up a Slack App and install it.

Create a Slack App

Start by logging into your Slack team or create a new one for the purpose of this tutorial. Visit the Slack API Console to create a new Slack App.

You'll need to have a Slack team or instance where we can install and test the app. I'm naming this Slack App after our make-believe e-commerce platform named Shop Quick.

Once we have created the app in our team workspace, we can put the Slack app to the side for the moment and work on creating the 8base function that listens for new orders on the Shop Quick platform.

Create an 8base function

8base’s platform provides features that make creating serverless applications easy. For this Slack App, we have a need for custom logic and integrations that can't be satisfied using CRUD only. In our case, we wish to send Slack updates whenever an order is created. We can achieve that with the help of 8base functions by listening for Create, Update and Delete events using triggers.

A trigger is a function that runs in response to a data mutation event: while creating, updating or deleting an object. There are two types of triggers based on whether they run before or after the data is modified: trigger.before and trigger.after.

In our case, we will be invoking our trigger function after an Order is created. Thus, we must specify Orders.create. Possible values for the data event are create, update and delete.

8base CLI

8base CLI allows you to write custom JavaScript or TypeScript to efficiently add logic, focusing only on what is unique to your application while bypassing any redundant boilerplate and plumbing.

The 8base Command Line Interface (CLI) makes it easy to add custom backend logic to your apps straight from the terminal. Install the CLI by running the following command in a terminal.

{% code-block language="sh" %}
npm install -g 8base
{% code-block-end %}

After installing the CLI globally, you have to log in to give the CLI access to your 8base account. Run the following command to login.

{% code-block language="sh" %}
8base login
{% code-block-end %}


Running this command will pop a browser window to authorise the CLI. Logging in grants the CLI access to your 8base account. You can find other commands offered by the CLI by running the help command:

{% code-block language="sh" %}
8base help
{% code-block-end %}

Next, we'll initialize an 8base project using the CLI, run the following command in your terminal:

{% code-block language="sh" %}
8base init shop-quick-functions
{% code-block-end %}

A new folder should be created after running the command. The folder structure should look like this:

{% code-block language="yml" %}
├── src/
|   ├── function1.ts
|   └── function2.js
├── 8base.yml
└── package.json
{% code-block-end %}

The CLI bootstraps a regular Node project that allows for dependencies to be installed using npm or yarn. Also, 8base CLI comes with Webpack and TypeScript pre-configured out of the box so you can write code in JavaScript or TypeScript.


The 8base.yml is where we configure custom functions and permissions. We'll alter the current setup of the 8base.yml file to reflect our needs. Open the file and make sure it includes the following changes:

{% code-block language="yml" %}
functions:  
  notifySlack:
    handler:      
     code: src/notifySlack.js    
   type: trigger.after    
   operation: Orders.create
{% code-block-end %}


The file structure is similar to any YAML file. The first block we’ll look at is the  functions block; we'll place all the function definitions we create within this block. For example, the notifySlack function, which we created; the function has the following properties:


  • handler: relative file path to the notifySlack function handler.
  • operation: parameter defines what data type and event the trigger should listen to.
  • type: whether they run before or after the data is modified. There are two types: trigger.before and trigger.after.


The next step is creating the handler file to start receiving events after a trigger. To send updates to Slack, we'll be making use of the chat.postMessage Method. The messages will be sent using an HTTP post method — each request to the chat.postMessage method will be signed by your Bot's OAuth access token. Visit your app's details page, click on the OAuth & Permissions link on the side navigation bar.


Copy the token and head over the Environment Variables tab in the 8base settings page. Your functions have access to all environment variables created. ALWAYS store your sensitive values and credentials here rather than placing them directly into source code base. Create a new variable named SLACK_ACCESS_TOKEN, paste the token you copied and save it.

Now let's actually put code to editor. Create a file named utils.js in the src directory. Open the file with your favorite editor and copy the following into it:

{% code-block language="js"%}
/* src/utils.js */export
const sendMessage = (order) => {
const message = `A new order was just created`;
const messageBody = dataBuilder(order);  
const body = {
channel: 'general',
attachments: messageBody,
text: message
};

       console.log('Message body', { body });  
console.log('Sending message request');  return makeRequest(body);
};‍
{% code-block-end %}

So the sendMessage function takes a single argument order which is an object containing the fields we created for the Order model. Now we need to curate a new message using the order object, so we pass the object to function called dataBuilder. The dataBuilder function returns an object in the format of Slack attachments.


Next, we create the request body which contains the workspace channel we're posting the message to, the attachments returned from the dataBuilder and finally the text. Finally, the request to post the message is sent by the makeRequest function.


Let’s create the dataBuilder function, copy the snippet below and update the utils.js file:

{% code-block language="js"%}
/* src/utils.js */
export const sendMessage = (order) => { ...};
const dataBuilder = (order) => {
 const { items } = order;
 const itemSections = items.map((item) => {
   return {
     type: 'section',
     text: item.name,
     color: '#3AA3E3',
     accessory: {
       type: 'image',
       image_url: item.image,
       alt_text: item.name,
     },
   };
 });
 const data = [
   {
     type: 'section',
     text: {
       type: 'mrkdwn',
       text: `*There are ${items.length} items in the order*`,
     },
   },
   ...itemSections,
 ];
 return data;
};
{% code-block-end %}


The dataBuilder function takes the order object and creates a section for each item in order using the item's name and image. You can use the Message Builder to preview your message formatting and attachments in real time. That's what I did to come up with this.


Next, we'll add the makeRequest function that posts the message body to the Slack workspace. Copy the following snippet into the utils.js file:

{% code-block language="js"%}
/* src/utils.js */
import fetch from 'node-fetch';
export const sendMessage = (order) => { ... };
const dataBuilder = (order) => { ... };
const makeRequest = (data) => {
const url = 'https://slack.com/api/chat.postMessage';
const headers = {
Authorization: `Bearer ${process.env.SLACK_ACCESS_TOKEN}`,
'Content-type': 'application/json',
};
return fetch(url, {
method: 'post',
body: JSON.stringify(data),
headers,
});
};
{% code-block-end %}

First, install the node-fetch package by running the following command:

{% code-block language="sh"%}
npm install node-fetch
{% code-block-end %}

We import the package and use it in the makeRequest function. Within the function we’ll add any required headers to the request. The request must be signed with a Bearer Authorization header; the token will be gotten from the environment variable we created earlier. Finally, we return a POST request using the fetch library.

We now have everything we need to send a message to a Slack workspace using the bot. Let's create the function handler that we referenced in the 8base.yml file above. Create a file called notifySlack.js in the src directory, open the file and copy the code below into it:

{% code-block language="js"%}
/* src/notifySlack.js */
import {
   sendMessage
} from './utils';
module.exports = async (event) => {
   try {
       console.log('Sending notification to slack');
       const res = await sendMessage(event.data);
       console.log('Notification sent successfully');
   } catch (error) {
       console.log(error);
   }
   return {
       data: event.data,
   };
};
{% code-block-end %}

In the file, we use the sendMessage utility function we created. An 8base function handler passes two arguments: event and context. The structure of the event object depends on the type of the function, and the context object contains useful properties and methods you can read about here.

In our case, the event object will contain an Order created, which consists of two fields user and items. The order object is the data property of the event object, and we pass that object to the sendMessage function to notify Slack.

The function call is wrapped by a try/catch because we won't want any errors causing the function to exit. Finally, we return the event data at the end of the function.

With this data, we'll curate the email subject and message and then send the mail using the transporter we created. Save the file and deploy the function using the 8base CLI by running the following command:

{% code-block language="sh"%}
# deploy while in project root directory
8base deploy
{% code-block-end %}

You should see the following terminal output once the function has successfully deployed:


{% code-block language="sh"%}
➜ 8base deploy
deploy done. Time: 15,553 ms.
{% code-block-end %}

Debugging functions

It is important to have logs in your functions, so you can easily debug the functions and keep track of what is going on there. 8base has provided a page to view all functions attached to your profile. You can easily view all the logs coming from each function.

Go to your 8base Dashboard and then click on the Logic button on the sidebar menu. In the Logic page, you can view all of your deployed functions.

For a specific function, click on View Logs to see all the logs. Next, we will get to install the Slack app and test it.

Install Slack App and test

Let's complete the final step in the process and install the Slack App to a workspace. To do this, head back over to the Slack app console and click on Install App on the side navigation bar.



On the page, you'll see an alert to add a feature or permission scope for installing the app. Click on that and add the permission.

After installing the app successfully, we can now test the function and the app. Head over to the 8base Data page, click on the Orders table on the sidebar. On the Order table viewer, click on the DATA tab, here we can add new rows to the table.


On the data table, click on the {+} button and create a new row.

You can populate the items field with the following data:

{% code-block language="json"%}
[{
   "name": "Food product",
   "image": "https://static.pexels.com/photos/368893/pexels-photo-368893.jpeg",
   "price": 4
}, {
   "name": "The real fruit",
   "image": "https://static.pexels.com/photos/368893/pexels-photo-368883.jpeg",
   "price": 20
}]
{% code-block-end %}



Click on Add Row to create a new order. When this is done, head over to the workspace where you installed the Slack App, click on the #general channel and you should see a new message from the bot. It should look like this:


New notification from 8base bot in Slack channel


Final words

Creating a Slack App is always fun; Slack is the primary means of communication for most teams and having all the information you require for a day's work on there can be useful.


For this article, we created a simple Slack App using 8base functions that posted messages about new orders in the system. The messages posted were fairly basic. Slack has rich formatting for their messages and you can utilize that in making your messages more complex to contain more information. You can read more on 8base functions here and you can find the code for this article on GitHub.

Thanks for reading.

Ready to try 8base?

Sign up for free or simply stay in touch.