Bolt-js: Health check for the app to deploy as a service

Created on 9 Oct 2019  路  11Comments  路  Source: slackapi/bolt-js

Description

I am trying to deploy my Slack application on AWS using an ECS service and a load balancer

In order to successfully deploy my app, I need to setup a health check when creating my target group. The health checks could be performed ~using several protocols~ in HTTP or HTTPS, but right now nothing works as the app would simply return a 502 (and 502 cannot be used as a valid return code to pass the health check) because of an unmatched path

How do I successfully check that my application is running from the outside and from a health check perspective ?

What type of issue is this? (place an x in one of the [ ])

  • [ ] bug
  • [ ] enhancement (feature request)
  • [ ] question
  • [ ] documentation related
  • [ ] testing related
  • [x] discussion

Requirements (place an x in each of the [ ])

  • [x] I've read and understood the Contributing guidelines and have done my best effort to follow them.
  • [x] I've read and agree to the Code of Conduct.
  • [x] I've searched for any related issues and avoided creating a duplicate issue.
discussion

Most helpful comment

@thehme Another way to safely access app in TS is to manually create an ExpressReceiver as below. (see also: https://github.com/slackapi/bolt/issues/191#issuecomment-494148037 )

const expressReceiver = new ExpressReceiver({
  signingSecret: process.env.SLACK_SIGNING_SECRET
});
const app: App = new App({
  token: process.env.SLACK_BOT_TOKEN,
  receiver: expressReceiver
});
expressReceiver.app.get('/', (_req: Request, res: Response) => {
  res.status(200).send();
});

@Startouf I verified the following code also works. There is not much difference, though.

const { App, ExpressReceiver } = require('@slack/bolt');
const receiver = new ExpressReceiver({
  signingSecret: process.env.SLACK_SIGNING_SECRET
});
const app = new App({
  token: process.env.SLACK_BOT_TOKEN, // single team authorization
  receiver: receiver
});

// health check for ALB
receiver.app.get('/', (_, res) => {
  res.status(200).send(); // respond 200 OK to the default health check method
});

I believe going with implementing additional endpoints in this way is not bad. But, if we have kinds of add-on packages that make Bolt integrations with major platforms smoother, that would be great regardless of whether such add-ons are owned in the Bolt project or not.

All 11 comments

Having the same issue here, how can we work around this? or is this just not documented?

@thehme It's possible to access the underlying express app to add a health check, here is the code

Assuming app is the bolt app, you can add a health check this way using app.receiver.app which is the actual express app.

import express from 'express';

const healthcheckRouter = express.Router();
healthcheckRouter.get('/healthcheck', function(req, res, next) {
  res.json({ status: 'UP' });
});
app.receiver.app.use(healthcheckRouter);

Note that I had to use an old style function declaration as (req, res, next) => { res.json({ status: 'UP' }); } would transpile incorrectly cf https://github.com/expressjs/express/issues/3515

@Startouf thanks for posting your workaround. I am trying it, but receiver is a private method on the app class, so how did you get around that? I am using ts:

import * as express from "express";
const healthCheckRouter = express.Router();
healthCheckRouter.get('/healthcheck', (req, res, next) => {
    res.json({ status: 200 });
});

app.receiver.app.use(healthCheckRouter);

Property 'receiver' is private and only accessible within class 'App'.

Ha, well I'm not using typescript so I did not run into this. Isn't there a way to bypass type checking temporarily until we get some feedback from the main developers ?

It would make a lot of sense to be able to use directly the underlying express app to do this kind of things. Also, there is an issue opened regarding the export of bolt as an express middleware

Btw my colleague suggested directly using an existing npm package

import healthcheck from 'healthcheck-middleware';

app.receiver.app.use('/healthcheck', healthcheck());

@Startouf yes, you are right, if I cast app as any, then I don't get the private method error, but lose the type checking. It's not the ideal thing I'd like to do, but it sort of beats re-writing this.

@thehme i'm using typescript as well, instead of casting the app to any i did this
basically the gist is to extend the ExpressReceiver from the bolt framework, from there you can add middlewares/additional routes

class MyCustomReceiver extends ExpressReceiver {
  constructor(options: ExpressReceiverOptions) {
    super(options);
    this.app.use(bodyParser.json());

    // have access to this.app here
    // this.app.get('/healthcheck',.....

@thehme Another way to safely access app in TS is to manually create an ExpressReceiver as below. (see also: https://github.com/slackapi/bolt/issues/191#issuecomment-494148037 )

const expressReceiver = new ExpressReceiver({
  signingSecret: process.env.SLACK_SIGNING_SECRET
});
const app: App = new App({
  token: process.env.SLACK_BOT_TOKEN,
  receiver: expressReceiver
});
expressReceiver.app.get('/', (_req: Request, res: Response) => {
  res.status(200).send();
});

@Startouf I verified the following code also works. There is not much difference, though.

const { App, ExpressReceiver } = require('@slack/bolt');
const receiver = new ExpressReceiver({
  signingSecret: process.env.SLACK_SIGNING_SECRET
});
const app = new App({
  token: process.env.SLACK_BOT_TOKEN, // single team authorization
  receiver: receiver
});

// health check for ALB
receiver.app.get('/', (_, res) => {
  res.status(200).send(); // respond 200 OK to the default health check method
});

I believe going with implementing additional endpoints in this way is not bad. But, if we have kinds of add-on packages that make Bolt integrations with major platforms smoother, that would be great regardless of whether such add-ons are owned in the Bolt project or not.

Thanks @josh91hickman and @seratch will look into these

I believe that @seratch comment should be mentioned somewhere in the docs, as it could be quite a common use case where your bolt bot should expose also some extra non-slack http endpoints.
I know that using javascript you can do [bolt instance].receiver.app but it's dirty.

The latest recommended way is to use receiver.router along with ExpressReceiver for registering new endpoints. Refer to https://slack.dev/bolt-js/concepts#custom-routes for details.

Let me close this issue now.

Was this page helpful?
0 / 5 - 0 ratings