Integrate Slack to your Nodejs app in Ten Minutes
Tags: slack, productivity, slackbot, slackapi
Published: 2020-02-05 23:53:52

Slack is a virtual office. Businesses, teams, and companies rely on it for communications and for managing daily activities. Activities such as placing orders for lunch, or contacting the front desk team or placing any form of request on the company's website can be carried out via the Slack workspace using Slack integrations via the [Slack API](https://api.slack.com/).

In this article, I will be showing how you can build an integration for sending requests from the Slack workspace to a given website. We will build a simple Nodejs server to test the integration.


## Requirements

* *Knowledge of Node.js/Express*
* *Slack workspace*: If you don't have a Slack workspace, you can create one for test purposes.

* *[Ngrok](https://ngrok.com/download)*: Since we will be working with a local development server, we will need ngrok to provision a public URL from the API in order to be able to communicate with Slack. Visit the [link](https://ngrok.com/download) to install it or you can install it globally via npm [here](https://www.npmjs.com/package/ngrok)

With those requirements above met, we can proceed to get the stuff done following the steps below:

## *Create Slack App*
In order to interact with the Slack API, you need a [Slack app](https://api.slack.com/apps). To create a Slack app, you need a Slack account (a workspace). Click [here](https://api.slack.com/apps) to create a Slack app.

![Create Slack app](https://thepracticaldev.s3.amazonaws.com/i/2e3erbn13ahyhnae4rl2.png)

 Next up is to generate an access token for authorizing requests between the website and the Slack workspace. You will get the token by clicking **OAuth & Permissions** 

Next up, we need to create a **slash command (/frontdesk)**. The slash command would pop up a modal when a user invokes it anywhere on the Slack workspace. But before creating the slash command, let's spin up an Express server. If you already have a server running you can skip to *_Creating Slash Command_*. The reason for having a running server at this point is that the slash command requires the endpoint for sending payloads to the website when invoked.

> Clone the project used in this article [here](https://github.com/nedssoft/slack-bot-nodejs.git) to follow along 

In `server/index.js`, I set up a simple Express server as shown in the snippet below:

```js
const express = require('express');
const bodyParser = require('body-parser');
const routes = require('../routes/');

const app = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

app.get('/', (req, res) => {
    res.json({ message: 'The app is running...'})
})

app.use('/api', routes);

module.exports = app;
```
Note: You must use the `bodyParser.urlencoded()` middle. This is needed to parse the url-encoded payload from Slack.


## **Slash command**
Slash command is a means to trigger an action from the Slack workspace. In our case, we want a user to be able to contact the front desk by invoking the `/frontdesk` command on Slack. When a user types the command `/frontdesk` and hits enter; an action is triggered and Slack will notify your website through an endpoint you provided while creating the slash command. In our case, the endpoint simply returns a modal with a form for the user to submit his/her request.

## *Creating the slash command endpoint*
 The code for the slash command endpoint is as shown below:

```js
router.post('/slack/frontdesk', (req, res) => {
  const { trigger_id: triggerId } = req.body;

  res.status(200).send('');
  (async () => {
    // Open a modal.
    await web.views.open({
      trigger_id: triggerId,
      view: {
        type: 'modal',
        title: {
          type: 'plain_text',
          text: 'Contact Front Desk',
        },
        submit: {
          type: 'plain_text',
          text: 'Submit',
        },
        callback_id: 'frontdesk',
        blocks: [
          {
            type: 'section',
            text: {
              type: 'plain_text',
              text: ':wave: We will get back to you as soon as possible',
              emoji: true,
            },
          },
          {
            type: 'divider',
          },

          {
            type: 'input',
            block_id: 'title',
            label: {
              type: 'plain_text',
              text: 'Title',
              emoji: true,
            },
            element: {
              type: 'plain_text_input',
              multiline: false,
              action_id: 'title',
            },
          },
          {
            type: 'input',
            block_id: 'description',
            label: {
              type: 'plain_text',
              text: 'Description',
              emoji: true,
            },
            element: {
              type: 'plain_text_input',
              multiline: true,
              action_id: 'description',
            },
            optional: true,
          },
        ],
      },
    });
  })();
});
```
### **Things to note from the slash command endpoint**
* The `req.body` contains among other payloads the `trigger_id`, the `trigger_id` is an identifier for the source of the command. In order to send the modal form to the user that invoked the command, we need the `trigger_id` so that the modal pops up at the exact channel.
* I used the [@slack/web-api](https://www.npmjs.com/package/@slack/web-api) which makes sending HTTP request to the Slack API seamless.
* I used the [Slack modal](https://api.slack.com/surfaces/modals), read more about it.
* I used the [Block Kit](https://api.slack.com/block-kit) to generate the modal form, read more about it.
* Note that each block has `block_id` and `action_id`, you'll find the reason shortly.
* The line `res.status(200).send('');` is a way to tell slack that the trigger was received successfully. Failure to return a response of 200 would cause Slack trigger an error.

## **Handling Interactions**
When the user submits the form, Slack sends the payload to an endpoint that you specified for it. This means that we need another endpoint for this purpose. The code for the interactions endpoint is as shown below:

```js
router.post('/slack/interactions', (req, res) => {
  res.status(200).send('');

  const payload = JSON.parse(req.body.payload);

  // view the payload on console
  console.log(payload);

  if (
    payload.type === 'view_submission' &&
    payload.view.callback_id === 'frontdesk'
  ) {
    const { values } = payload.view.state;
    const title = values.title.title.value;
    const description = values.description.description.value;

    console.log(`title ----->${title}`, `description---->${description}`);

  // Save the title and description to the database or handle it as you may deem fit.
  }
});
```

The form payload is located at the `payload` object of the `req.body`. The form values are located in the `payload.view.state` object.
Notice the long chain in order to extract the form input value, that is due to the `block_id` and `action_id` used in building the form, without it, the form values would not be deterministic as the `block_id` and `action_id` would be random values.

Lastly, we need to create the actual slash command on the slack app we created earlier and also specify the interactions endpoint. To do so, we need to start the server and provision a public URL using `ngrok`.

```
npm start
```
Next up, navigate to the folder where the ngrok was unzipped and run the command below:

```
./ngrok http 3000
```
If you installed ngrok globally, you can run the command below anywhere on the terminal
```
ngrok http 3000 
```
Note that `3000` is the port the server is listening, you can change it to the port yours is running.
The screenshot below shows the commands above in action:

![Image showing running server and ngrok](https://dev-to-uploads.s3.amazonaws.com/i/cep1onxulz2ab2sr2ogi.png)

Now that we have provisioned a public URL from the local server, the slash command and the interactions endpoints would be ` https://38a73f75.ngrok.io/api/slack/frontdesk` and ` https://38a73f75.ngrok.io/api/slack/interactions` respectively. Let's proceed to create the slash command.

Head to the Slack app you created earlier as shown below:

![Create slash command page](https://dev-to-uploads.s3.amazonaws.com/i/vb62soj57wr3qwy5ae61.png)

Fill the form as shown below:

![Create a slash command form](https://dev-to-uploads.s3.amazonaws.com/i/4h0ow7dz6dkc8x281uow.png)

Next up, click the *Interactive Components* to add the interactions endpoint as shown below:

![Interation components page](https://dev-to-uploads.s3.amazonaws.com/i/8cb36ziounai006acfe8.png)

It is time to test it! Head over to the Slack workspace and invoke the `/frontdesk` command, you'll see a modal as shown below:

![Modal popped up when `/frontdesk` is invoked](https://dev-to-uploads.s3.amazonaws.com/i/btcsj5y65c7tb1v5860p.png)

The result of submitting the form is shown below as logged on the console:

![The console showing the output of the interaction endpoint](https://dev-to-uploads.s3.amazonaws.com/i/9dm6el7rxprr6njb0ol0.png)

## Conclusion
I tried to make this article as simple as possible, hence, the snippets may not have followed best practices, feel free to refactor it to suit your needs. 

If you have some suggestions that would further make the article easier to the readers, you are welcome to do so. Gracias!

 _Originally published in my [blog](https://nedsoft.dev/posts/integrate-slack-to-your-nodejs-app-in-ten-minutes/)_