Backend Development 19 min read

Building a Slackbot with Node.js and Slack's Bolt API

This tutorial walks through creating a Slackbot using the new Bolt API with Node.js, covering workspace setup, app creation, OAuth scopes, server configuration, code installation, environment variables, socket mode, slash commands, event subscriptions, a simple JSON knowledge‑base, and deployment steps.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Building a Slackbot with Node.js and Slack's Bolt API

New Bolt API

I recently tried developing a Slackbot with the Node Slack SDK and discovered the new Bolt API, which Slack describes as the fastest way to start programming on the Slack platform using JavaScript.

The Bolt API is well‑documented, maintained by Slack, and helps avoid obscure errors, making it the recommended path for building Slack apps.

Setup

Prerequisites

Basic knowledge of JavaScript and Node.js

Node.js v12 or higher

npm

Create a Workspace

Install Slack, create a new workspace, verify with the emailed 6‑digit code, and give the workspace a name (e.g., "The Zobo Tea Company").

Create a New Slack App

In the Slack API dashboard click Create New App , name it ask-ztc-bot , and select the workspace.

OAuth & Permissions

Set the required scopes for ask-ztc-bot (e.g., reading user messages) and obtain the SLACK_SIGNING_SECRET and SLACK_BOT_TOKEN from the Basic Information and OAuth sections.

Set Up the Slackbot Server

Create a directory ask-ztc-bot and initialize npm:

mkdir ask-ztc-bot && cd ask-ztc-bot
npm init -y

Install required packages:

yarn add @slack/bolt
yarn add -D nodemon dotenv

Add a dev script to package.json to run nodemon app.js :

... 
  "scripts": { 
    "dev": "nodemon app.js" 
  }, 
...

Create app.js with the following server code:

const { App } = require("@slack/bolt");
require("dotenv").config();
const app = new App({
  token: process.env.SLACK_BOT_TOKEN,
  signingSecret: process.env.SLACK_SIGNING_SECRET,
});
(async () => {
  const port = 3000;
  await app.start(process.env.PORT || port);
  console.log(`⚡ Slack Bolt app is running on port ${port}!`);
})();

Set Up ngrok

Expose the local server to the internet:

./ngrok http 3000

Socket Mode (Optional)

Enable Socket Mode in the Slack app settings, generate an app‑level token with connections:write and authorizations:read , and update the app initialization:

const app = new App({
  token: process.env.SLACK_BOT_TOKEN,
  signingSecret: process.env.SLACK_SIGNING_SECRET,
  socketMode: true,
  appToken: process.env.APP_TOKEN
});

Slash Commands

Create a /knowledge command in the Slack dashboard, pointing the request URL to the ngrok URL.

Add the command handler:

app.command("/knowledge", async ({ command, ack, say }) => {
  try {
    await ack();
    say("Yaaay! that command works!");
  } catch (error) {
    console.error(error);
  }
});

Message Listeners

Listen for mentions and keywords:

app.message("hey", async ({ say }) => {
  say("Yaaay! that command works!");
});

app.message(/hey/, async ({ say }) => {
  say("Yaaay! that command works!");
});

Create a Knowledge Base

Store FAQs in a simple db.json file:

{
  "data": [
    {"keyword": "products", "question": "How many different products do we sell at ZTC?", "answer": "ZTC currently has 3 products..."},
    {"keyword": "products", "question": "What online stores sell our products?", "answer": "Amazon, Macy's and Shoprite."},
    {"keyword": "people", "question": "How many people work at ZTC?", "answer": "250 people from 21 countries."},
    {"keyword": "reset password", "question": "How do I reset my password?", "answer": "Call Ola on ext.8099."}
  ]
}

Read the JSON in app.js :

const fs = require('fs');
let raw = fs.readFileSync('db.json');
let faqs = JSON.parse(raw);

Respond to /knowledge by iterating over faqs.data and building Slack blocks.

app.command("/knowledge", async ({ ack, say }) => {
  await ack();
  let message = { blocks: [] };
  faqs.data.forEach(faq => {
    message.blocks.push({ type: "section", text: { type: "mrkdwn", text: "*Question*" } });
    message.blocks.push({ type: "section", text: { type: "mrkdwn", text: faq.question } });
    message.blocks.push({ type: "section", text: { type: "mrkdwn", text: "*Answer*" } });
    message.blocks.push({ type: "section", text: { type: "mrkdwn", text: faq.answer } });
  });
  say(message);
});

Filter by keyword (e.g., products ) using a regex listener:

app.message(/products/, async ({ say }) => {
  let message = { blocks: [] };
  const productsFAQs = faqs.data.filter(faq => faq.keyword === "products");
  productsFAQs.forEach(faq => {
    // push blocks similar to above
  });
  say(message);
});

Update Knowledge Base

Create a /update slash command that splits the user input by | and appends a new FAQ to db.json :

app.command("/update", async ({ command, ack, say }) => {
  await ack();
  const data = command.text.split("|");
  const newFAQ = { keyword: data[0].trim(), question: data[1].trim(), answer: data[2].trim() };
  fs.readFile("db.json", (err, fileData) => {
    const json = JSON.parse(fileData);
    json.data.push(newFAQ);
    fs.writeFile("db.json", JSON.stringify(json), err => {
      if (err) throw err;
      console.log("Successfully saved to db.json!");
    });
  });
  say(`You've added a new FAQ with the keyword *${newFAQ.keyword}*.`);
});

Deployment

Deploy the Node.js app to platforms like Heroku, updating the event subscription URL to the new public endpoint.

Refer to the Bolt API documentation for further details.

backendNode.jsChatbotBolt APISlack
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.