How to Add Custom HTTP Routes with Slack Bolt.js

Burak Sonmez

Burak Sonmez / February 26, 2023

3 min read––– views

Slack Bolt.js is a powerful library for building Slack apps and bots. It simplifies the process of interacting with the Slack API and provides useful tools and features for creating robust applications. One such feature is the ability to add custom HTTP routes.

As of version 3.7.0, adding custom HTTP routes is easy with Slack Bolt.js. You can do this by passing an array of routes as customRoutes when initializing your App instance. Each CustomRoute object in the array must contain three properties: path, method, and handler. method corresponds to the HTTP verb and can be either a string or an array of strings.

Here's an example of how to add a custom HTTP route to your Slack Bolt.js app:

const { App } = require('@slack/bolt');

const app = new App({
  token: process.env.SLACK_BOT_TOKEN,
  signingSecret: process.env.SLACK_SIGNING_SECRET,
  customRoutes: [
    {
      path: '/health-check',
      method: ['GET'],
      handler: (req, res) => {
        res.writeHead(200);
        res.end('Health check information displayed here!');
      }
    }
  ]
});

(async () => {
  await app.start();
  console.log('⚡️ Bolt app started');
})();

In the example above, we create a new instance of the App class and pass in an array of custom routes. The route we define has a path of /health-check, a method of GET, and a handler function that returns a simple response to the client.

By default, custom routes will listen on port 3000. However, you can specify a different port by adding an installerOptions property to the App constructor.

Adding custom HTTP routes can be useful for implementing custom functionality in your Slack app. For example, you could create a route for a webhook that receives data from an external service, or a route for a health check endpoint that can be monitored by a third-party service.

Handle Body and Query Parameters

// routes.js
const routes = [
  {
    path: '/health-check',
    method: ['GET'],
    handler: require('./health-check')
  },
  {
    path: '/some-endpoint',
    method: ['POST'],
    handler: require('./some-endpoint')
  }
];
module.exports = routes;

Unfortunately, in this clean version, we cannot get req.body and req.query. To get the you can basically do the following.

// index.js
const { App } = require('@slack/bolt');

class Slack {
  constructor(props){
    this.db = props.db;
  }

const app = new App({
  token: process.env.SLACK_BOT_TOKEN,
  signingSecret: process.env.SLACK_SIGNING_SECRET,
  // customRoutes: routes
  customRoutes: routes.map((route) => ({
    ...route,
    handler: (req, res) => route.handler(req, res, this),
  }))
});

(async () => {
  await app.start();
  console.log('⚡️ Bolt app started');
})();
}

In this code, the customRoutes parameter is set to an array of custom routes created by mapping over an array called routes. Each route in routes is an object with properties such as path and handler, which define the URL path for the route and the function to handle requests to that path.

The map() function is used to transform each route object into a new object with an additional handler property that is a function. This new function is a wrapper that calls the original handler function with an additional this parameter, which is set to the Slack instance. This allows the handler to access the Slack instance and its properties, such as the db property.

By setting the customRoutes parameter to this array of custom routes, the app will handle incoming HTTP requests to the specified paths using the custom handler functions.