Slack DevKit is a light-weight developer kit to build Slack Apps in node.js super fast. No previous knowledge about building Slack Apps or Bots needed. Also includes automatic support for verifying requests from Slack and responding to validations.
This was made to build Slack Apps on Glitch and AWS Lambda even faster, but it works everywhere!
Highlights

- OAuth support without 3rd-party database
- Verifies request signatures and/or verification tokens
- Support for short-lived tokens with automatic refresh
- Automatic retrieval of workspace's authentication info
- Auto-parsing of string-encoded JSON payloads
- Authenticated HTTPS client for Slack's API
- Writeable datastore associated to each workspace
- Automatic Slack payload normalization
- Auto validates Events API challenges
- Supports Single Channel Installations
Installation
Slack DevKit can be installed using npm.
npm i slack-devkitOnce installed, you would require the package in your code.
const Slack = require('slack-devkit')Creating new instances of Slack DevKit return four objects that can be used as needed.
const { server, router, client, app, lambda } = new Slack(settings)
Settings
Single Workspace Apps
Apps that are only used in one workspace, also known as Internal Integrations, simply require a Slack Access Token and a way to verify the request originated from Slack.
Apps that are only used in one workspace, also known as Internal Integrations, simply require a Slack Access Token and a way to verify the request originated from Slack.
new Slack({
access_token: "xoxa-XXXXXXXXXXXXX",
signing_secret: "XXXXXXXXXXXXXXXX"
})
new Slack({
scope: 'commands,files:write,links:read,links:write',
client_id: "1212313.1231231231231",
client_secret: "12312312323123123",
signing_secret: "sdfsadfsadfasdfas"
})
Here's a list of all possible settings that can be used to configure Slack DevKit.
| Property | Description |
|---|---|
scope | Slack OAuth scopes |
client_id | Slack OAuth client id |
client_secret | Slack OAuth client secret |
redirect_uri | Slack OAuth redirect uri |
signing_secret | Slack signing secret |
verification_token | Slack verification token |
access_token | Access token to use for Internal Integrations |
datastore | A file path to write the datastore to or pass in an object to use an alternate datastore |
slack_root | The root domain to make Slack requests to |
All arguments are optional, but may be required based on the App type.
Server
const { server } = new Slack(settings)
...// routing stuff
// optionally pass in the port number to listen on
// server will be listening on localhost:3000
server.start(3000)
LocalTunnel
The server is preconfigured to use LocalTunnel if you've installed the module (it's not preloaded).
The server is preconfigured to use LocalTunnel if you've installed the module (it's not preloaded).
const { server } = new Slack(settings)
...// routing stuff
// optionally pass in the subdomain to use (xxxx.localtunnel.me)
// server will be listening on slackdevkit.localtunnel.me
server.startLocal('slackdevkit')
Requests
The Slack Request is populated on all incoming POST requests and will be attached to the Express.js request object. All helper methods on Payload can also be called from the Request.
server.post('/', (req, res) => {
req.slack // this is the Slack Request object
})
API
Make authenticated calls to the Slack API by calling the
Make authenticated calls to the Slack API by calling the
api method on requests.req.slack.api('users.info', { user: 'U12345' }).then(r => {
r.data // the api results
})
req.slack.reply({
text: 'This is a reply!',
attachments: [{
text: 'boom!'
}]
})
Unfurls
Respond to
Respond to
link_shared events and unfurl them with unfurl.req.slack.unfurl({
title: 'Unfurled Link',
image_url: 'https://home.com/image.png'
})
req.slack.upload({
filename: 'some_file.png',
file: fileBuffer
})
A few more payload helper methods that are used on request:
| Properties | Description |
|---|---|
action | Interactive message's selected action |
selection | The message's selected option |
text | The message text |
bot_id | The bot id the payload was sent from (if applicable) |
channel_id | The channel id the payload was sent from |
team_id | The team id the payload was sent from |
user_id | The user id that sent the payload |
event_types | Event types that triggered this payload |
| Methods | Description |
|---|---|
subcommand(name) | The sub-command arguments for a command |
isSubCommand(name) | Check if the a slash command contains a sub-command |
is(event_type) | Check if the payload is a type of event |
match(regex) | Match the message text against a regex |
Router
The Slack Router is an Express.js Router that can be used as middleware in existing Express.js apps.
const express = require('express'),
Slack = require('slack-devkit'),
app = express()
const { router } = new Slack({
scope: SCOPE,
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
signing_secret: SIGNING_SECRET
})
app.use(router)
Alternatively, you can pass the router into selected routes
app.get('/install', router, (req, res) => {
req.slack // populated
})
app.post('/events', router, (req, res) => {
req.slack // populated
})
Lambda
Slack DevKit has built-in support for AWS Lambda with DynamoDB . When constructing Slack DevKit, pass-in the DynamoDB table name to use in the
datastore property.const { lambda } = new Slack({
scope: SCOPE,
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
signing_secret: SIGNING_SECRET,
datastore: 'workspaces' // DynamoDB Table Name
})
exports.handler = lambda((slack, context, callback) => {
slack // the Slack Request object
// respond to lambda normally
return context.succeed(slack.data)
}Serverless.js
Slack DevKit's Lambda handler is easier to deploy using Serverless.js coupled with the included Serverless.js template.
Slack DevKit's Lambda handler is easier to deploy using Serverless.js coupled with the included Serverless.js template.
DataStore
Usage
All requests from Slack come with a
All requests from Slack come with a
data object that contains authentication information about the workspace and any additional information you would like to save. This object gets populated after OAuth has been successful.server.post('/', (req, res) => {
// The DataStore associated with the calling workspace
req.slack.data
// The OAuth access information for the workspace is available here
const { access_token, team_id } = req.slack.data
// Set custom data key/values
req.slack.set('whatever_you_want', { isCool: true })
// Get the custom data
req.slack.get('whatever_you_want')
// access the object directly
req.slack.data.whatever_you_want
// update the whole thing - be careful though :)
const data = Object.assign({}, data, { isCool: true })
req.slack.update(data)
})
Custom Path
The default DataStore is JSON and written to file at
The default DataStore is JSON and written to file at
.data/workspaces. You can change this by passing in a new path to the datastore setting.new Slack({
scope: SCOPE,
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
signing_secret: SIGNING_SECRET,
datastore: 'data/all-my-dataz.json'
})
Custom DataStore
If you want something a little more powerful than a filestore, you can pass in your own custom DataStore object to
If you want something a little more powerful than a filestore, you can pass in your own custom DataStore object to
datastore and Slack DevKit will attempt to use that instead.class CustomDataStore {
constructor() {
this.data = {}
}
get(id) {
return Promise.resolve(this.data[id])
}
save(id, record) {
this.data[id] = record
return Promise.resolve(this.data[id])
}
update(id, record) {
Object.assign(this.data[id], record)
return Promise.resolve(this.data[id])
}
}
new Slack({
scope: SCOPE,
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
signing_secret: SIGNING_SECRET,
datastore: new CustomDataStore()
})
Sample Application
Here's a sample application using some commonly used Slack features
const { SCOPE, CLIENT_ID, CLIENT_SECRET, SIGNING_SECRET } = process.env
const Slack = require('slack-devkit')
// Configure express with the Slack App settings
const { server, client } = new Slack({
scope: SCOPE,
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
signing_secret: SIGNING_SECRET
})
// All GET routes redirect to the “Add to Slack” OAuth flow
server.get('/', (req, res) => {
// the req.slack object contains information about the request
// and the workspace's authentication information
const { data, app_url } = req.slack
// Make an authenticated request to the Slack API
req.slack.api('chat.postMessage', {
channel: data.installer_user.app_home,
text: 'Thanks for installing me :bow:'
})
// open the Slack client to the App Home
res.redirect(app_url)
})
// Slash Command and Events API routes automatically load the
// workspace info and related datastore
server.post('/slash-command', (req, res) => {
// check if a sub-command was sent
const isUpload = req.slack.isSubCommand('upload')
// test the message text with regex
const containsUrl = req.slack.match(/https?:\/\//i)
// use the included client to call other APIs or
// to load information from other sites
if (isUpload && containsUrl) {
// get the arguments from a sub-command
const fileUrl = req.slack.subcommand('upload')
client.get(fileUrl).then(r => {
const file = Buffer.from(r.data, 'utf8')
// one-line of code to respond to slash commands with a file
req.slack.upload({ filename: 'logo.png', file })
})
}
res.send()
})
// All POST routes expect Slack callback events
// and verify against the verification token
server.post('/', (req, res) => {
// check the event type with is()
const isUnfurl = req.slack.is('link_shared')
const isMessage = req.slack.is('message.app_home')
// respond with an unfurl easily
if (isUnfurl) {
req.slack.unfurl({
text: "A successful unfurl!",
image_url: "https://image.com"
})
}
// reply to messages everywhere
if (isMessage) {
req.slack.reply({
text: "Hey! I got your message :sunglasses:"
})
}
res.send()
})
// Start the webserver on port 3000
server.start(3000)

