Bolt-js: Integrating a Bolt app into another server or framework (upward modularity)

Created on 3 Jul 2019  路  11Comments  路  Source: slackapi/bolt-js

I would like to use bolt as an express middleware similar to how the @slack/events-api work.

app.use('/slack/events', slackEvents.expressMiddleware());

Is there an equivalent in bolt? Thanks

discussion

Most helpful comment

Hi!
I think it would be great if we could do something like:

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

const boltApp = new App({
      token: configuration.slackAccessToken,
      signingSecret: configuration.slackSigningSecret,
});

const boltMiddleware = boltApp.getMiddleware();
expressApp.use('/bolt', boltMiddleware);

All 11 comments

Any Progress on This issue?

I would also be interested as a possible way to solve https://github.com/slackapi/bolt/issues/283

is this related to expressReceiver ?

Is there any update on this?

I'm trying to implement it in my NestJS application but neither NestJS or Bolt allows me to pass an existing express instance.

Same boat as @regniblod - trying to combine Nestjs and bolt. Any update on this?

Hi folks! Integrating Bolt for JS into other HTTP servers/frameworks is something we're interested in making happen. Within the team, we've called this idea "upward modularity" since its about making Bolt fit inside a larger app. (It's probably not important but "downward modularity" would be about combining several parts of a Bolt app into one Bolt app).

We want this to work in a generic way, so that Bolt can integrate not only into NestJS, but into nearly any web server/framework (Express, hapi, plain Node http servers, Koa, etc). In fact, there's some prior work to integrate Bolt into a Koa application by @barlock's team here.

The way to move this topic forward would be with a proposal. If you have a specific idea for how you think this should work, please go ahead and write up a description. It doesn't need to be anything formal, just something to describe how you'd like to see the feature work. We can help suss out any questions that arise from that, and the community can help design a solution.

PS. If you just want to hook into Bolt's underlying express app by adding a few custom routes, you can already do that, but we need to document that better.

Meanwhile, this worked for me -

Extract the express app from bolt and add nestjs middleware.

import { App, ExpressReceiver } from '@slack/bolt';
import { AppMiddleware } from './nestj/app.middleware';

const receiver = new ExpressReceiver({ signingSecret: configuration.slackSigningSecret });

const app = new App({
      receiver,
      token: configuration.slackAccessToken,
      signingSecret: configuration.slackSigningSecret,
    });

    receiver.app.use((req, res, next) => {
      const nest = new AppMiddleware(app).use(req, res, next);
      nest.then(() => {
        next();
      }).catch(err => {
        next();
      });
    });

    // Start your app
    const port = configuration.port || 3000;
    await app.start(port);
    console.log("鈿★笍 Bolt app is running! on port " + port);

https://slack.dev/bolt-js/concepts#custom-routes


I followed this SO answer to implement the NestJS middleware.

app.middelware.ts

import { Injectable, NestMiddleware } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { ExpressAdapter } from '@nestjs/platform-express';
import { AppModule } from './app.module';

const bootstrap = async (express: Express.Application) => {
  const app = await NestFactory.create(AppModule, new ExpressAdapter(express));
  await app.init();
  return app;
}

@Injectable()
export class AppMiddleware implements NestMiddleware {

  constructor(private expressInstance: Express.Application) {}

  use(req: any, res: any, next: () => void) {
    console.log('In Nest middleware');
    return bootstrap(this.expressInstance);
  }
}

Hi!
I think it would be great if we could do something like:

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

const boltApp = new App({
      token: configuration.slackAccessToken,
      signingSecret: configuration.slackSigningSecret,
});

const boltMiddleware = boltApp.getMiddleware();
expressApp.use('/bolt', boltMiddleware);

Works fine for me:

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

const app = express();

const slackApp = new App({
    signingSecret: config.slackApp.signingSecret,
    token: config.slackApp.token,
    endpoints = '/'
});

app.use('/slack/events', boltApp.receiver.router); // works also with boltApp.receiver.app

You can add a path in the app.use and modify the Bolt endpoint(s).

To cover the Hapi framework v.17+ you can jerry-rig a solution by registering Bolt's provided Express Receiver as a plugin using the hecks package. It's not the most elegant solution but I needed something that worked quickly... Hope it helps someone!

```javascript
const Hapi = require('@hapi/hapi');
const Hecks = require('hecks');
const { App, ExpressReceiver } = require('@slack/bolt');

// INIT BOLT APP
const Receiver = new ExpressReceiver({ signingSecret: SLACK_SIGNING_SECRET });

const BoltApp = new App({
token: SLACK_BOT_TOKEN,
signingSecret: SLACK_SIGNING_SECRET,
receiver: Receiver
});

// INIT HAPI SERVER
const init = async () => {

const server = Hapi.server({
    port: 3000,
    host: 'localhost'
});

await server.register([Hecks.toPlugin(BoltApp.receiver.app, 'my-bolt-app')]);

server.route({
  method: '*',
  path: '/slack/events',
  handler: {
    express: BoltApp.receiver.app
  }

});

await server.start();
console.log('Server running on %s', server.info.uri);

};

process.on('unhandledRejection', (err) => {
console.log(err);
process.exit(1);
});

init();

Hi there!! Thank you for all of your advices.
My scenario is to combine multiple slack apps in a same server.
In TS + express, like this:
createApp.ts

import { App, ExpressReceiver } from '@slack/bolt';

export const createApp = (appName: string) => {
  const signingSecret = ...;
  const token = ...;
  const receiver = new ExpressReceiver({
    signingSecret,
    endpoints: {
      events: `/${appName}/slack/events`,
    },
  });

  const app = new App({
    token,
    receiver,
  });

  return { app, receiver };
};

one slack app: app-a.ts

import { createApp } from './createApp';

const { app, receiver } = createApp('app-a');

app.message('hello app-a', async ({ body, say }) => {
  await say(`Hey there, I'm app-a`);
});

export { receiver };

server.ts

import express from 'express';

import { receiver as appA } from './app-a';
import { receiver as appB } from './app-b';

const app = express();

// you can add more apps
app.use(appA.router); // App A's Event Subscription > Request URL is https://yourserver/app-a/slack/events
app.use(appB.router); // App B's Event Subscription > Request URL is https://yourserver/app-b/slack/events

app.listen(3030);
Was this page helpful?
0 / 5 - 0 ratings