Instructions for AI coding agents working on this repository.
Slack Bolt for JavaScript -- a framework for building Slack apps, fast.
- Foundation: Built on top of
@slack/web-api,@slack/oauth,@slack/socket-mode, and other@slack/*packages (seepackage.jsonfor the full list and versions). - Language: TypeScript-first, compiled to CommonJS.
- Node version: See
enginesinpackage.jsonfor minimum Node.js and npm versions. - Repository: https://github.com/slackapi/bolt-js
- Documentation: https://docs.slack.dev/tools/bolt-js/
- npm: https://www.npmjs.com/package/@slack/bolt
- Current version: defined in
package.json - Examples: Sample apps in
examples/(Socket Mode, OAuth, Lambda, custom receivers, etc.)
# Install all dependencies
npm installBefore considering any work complete, you MUST run npm test and confirm it passes.
npm test # Full pipeline: build -> lint -> type tests -> unit test coverage
npm run build # Clean build (rm dist/ + tsc compilation)
npm run lint # Biome check (formatting + linting)
npm run lint:fix # Biome auto-fix
npm run test:unit # Unit tests only (mocha)
npm run test:coverage # Unit tests with coverage (c8)
npm run test:types # Type definition tests (tsd)
npm run watch # Watch mode for development (rebuilds on src/ changes)Incoming events flow through a middleware chain before reaching listeners:
- Receiver ingests event from Slack (HTTP, Socket Mode, or Lambda)
- Receiver calls
App.processEvent(ReceiverEvent) - Global middleware chain executes (authorization, self-event ignoring, custom middleware)
- App determines event type and matches relevant listeners based on constraints
- Listener-specific middleware chains execute
- Listener handler runs with full context
For FaaS environments (processBeforeResponse: true), acknowledgment happens after the handler executes.
App(src/App.ts) -- Central orchestrator. Registers listeners via methods (app.event(),app.action(),app.command(), etc.). Dispatches incoming events through middleware to matching listeners. Manages the Web API client pool, authorization, and error handling.Receiverinterface (src/types/receiver.ts) -- Pluggable transport layer abstraction. Methods:init(app),start(),stop().HTTPReceiver(src/receivers/HTTPReceiver.ts) -- Express v5 HTTP server, default receiverSocketModeReceiver(src/receivers/SocketModeReceiver.ts) -- WebSocket-based, no public URL neededExpressReceiver(src/receivers/ExpressReceiver.ts) -- Integrates with an existing Express v5 appAwsLambdaReceiver(src/receivers/AwsLambdaReceiver.ts) -- AWS Lambda handler
Assistant(src/Assistant.ts) -- AI assistant thread handling middleware. Intercepts assistant thread events (assistant_thread_started,assistant_thread_context_changed,messagein assistant threads) and dispatches them to registered sub-handlers. Provides utilities:say,setStatus,setSuggestedPrompts,setTitle,getThreadContext,saveThreadContext. UsesAssistantThreadContextStore(src/AssistantThreadContextStore.ts) for thread context persistence.CustomFunction(src/CustomFunction.ts) -- Workflow custom function handler. Providescomplete()andfail()utilities for function execution lifecycle.WorkflowStep(src/WorkflowStep.ts) -- Deprecated. UseCustomFunctionandapp.function()instead.
Middleware uses a chain-of-responsibility pattern. Each middleware receives args and calls next() to continue the chain.
Type: Middleware<Args> = (args: Args & AllMiddlewareArgs) => Promise<void>
AllMiddlewareArgs (always available):
context-- Event metadata (botToken, userToken, botId, botUserId, teamId, enterpriseId, etc.)logger-- Logger instanceclient-- Web API client (pre-authorized)next-- Call to continue the middleware chain
Built-in middleware in src/middleware/builtin.ts includes constraint matchers (event type, command name, message pattern, action/shortcut/view constraints), type guards (onlyActions, onlyCommands, etc.), ignoreSelf, and autoAcknowledge.
| Method | Description | Must ack()? |
|---|---|---|
app.event(type, fn) |
Events API events | No |
app.message([pattern,] fn) |
Message events (optional string/RegExp filter) | No |
app.action(constraints, fn) |
Block Kit interactive actions (buttons, selects, etc.) | Yes |
app.command(name, fn) |
Slash commands | Yes |
app.shortcut(constraints, fn) |
Global and message shortcuts | Yes |
app.view(constraints, fn) |
Modal view_submission / view_closed | Yes |
app.options(constraints, fn) |
External data source requests | Yes |
app.function(callbackId, fn) |
Custom workflow function executions | Auto-acknowledged |
Listeners receive a single object with these properties (availability depends on event type):
payload/ type-specific alias (event,action,command,shortcut,view,options,message) -- The incoming event datasay-- Send a message to the associated channelack-- Acknowledge receipt of the event (required for interactive events within 3 seconds)respond-- Respond viaresponse_urlclient-- Pre-authorized Web API clientcontext-- Event metadata and authorization infobody-- Full request body from Slackcomplete/fail-- Workflow function completion (forapp.function())
- Single workspace: Provide
tokeninAppOptions-- used for all events. - Multi-workspace: Provide an
authorizefunction that receivesAuthorizeSourceData(teamId, enterpriseId, userId, conversationId, isEnterpriseInstall) and returnsAuthorizeResult(botToken, userToken, botId, botUserId, etc.). - OAuth support via
@slack/oauth-- configureclientId,clientSecret,stateSecret,scopes,installationStoreinAppOptions.
- TypeScript throughout. Compiler options in
tsconfig.json(extends@tsconfig/node18, CommonJS output). - Biome for formatting and linting. Configuration in
biome.json. - Testing: See the Testing section below for test frameworks and conventions.
- Use Biome exclusively -- never ESLint or Prettier. Config is in
biome.json. - Run
npm testbefore submitting -- this runs the full pipeline (build + lint + types + coverage). - Follow existing patterns -- when adding new listener types, middleware, or receivers, match the structure and style of existing implementations.
- Don't duplicate
package.jsonvalues -- reference it for versions, engines, and dependency lists. - Don't add
WorkflowStepcode -- it is deprecated. UseCustomFunctionandapp.function()instead. - Build before running unit tests directly --
npm testhandles this automatically, butnpm run test:unitrequires a build to exist first. - Keep the Receiver abstraction clean -- receivers should only handle transport concerns (ingesting events, sending ack responses). Business logic belongs in middleware and listeners.
- Prefer middleware for cross-cutting concerns -- authorization, logging, validation, and feature-level request handling (like
Assistant) all use the middleware pattern. - TypeScript types are part of the API -- changes to exported types are breaking changes. Add type tests for new public types.
- Every listener type needs four things: type definitions, built-in middleware matchers, an App method, and tests.
This is one of the more complex contribution patterns. Follow these steps:
- Define types in
src/types/-- create the event-specific middleware args interface (see existing patterns insrc/types/actions/,src/types/events/, etc.). - Add built-in middleware matchers in
src/middleware/builtin.ts-- create a constraint-matching function and a type guard (e.g.,onlyMyType). - Add the listener method in
src/App.ts-- follow the pattern of existing methods likeaction(),command(), etc. Wire up the middleware matchers and register listeners. - Export types from
src/types/index.tsandsrc/index.ts. - Add unit tests in
test/unit/mirroring the source structure. - Add type tests in
test/types/using tsd.
- Implement the middleware function with signature
(args: MiddlewareArgs & AllMiddlewareArgs) => Promise<void>. Callawait next()to continue the chain. - For built-in middleware, add to
src/middleware/builtin.tsand export fromsrc/middleware/. - For complex middleware (like
Assistant), create a dedicated file insrc/with a class that provides agetMiddleware()method returning the middleware function. - Register in the App's middleware chain in
src/App.tswhere the default middleware is assembled. - Add tests in
test/unit/middleware/.
Tests mirror the source directory structure:
test/unit/
App/ # App class tests
middleware/ # Middleware tests
receivers/ # Receiver tests
helpers/ # Helper function tests
Assistant.spec.ts
CustomFunction.spec.ts
conversation-store.spec.ts
...
test/types/ # tsd type tests
- Test files use
*.spec.tssuffix - Assertions use chai (
expect,assert) - Mocking uses sinon (
stub,spy,fake) and proxyquire for module-level dependency replacement - Test config in
test/unit/.mocharc.json - Where to put new tests: Mirror the source structure. For
src/Foo.ts, addtest/unit/Foo.spec.ts. Forsrc/receivers/Bar.ts, addtest/unit/receivers/Bar.spec.ts.
CI configuration is in .github/workflows/ci-build.yml. Tests run across multiple Node.js versions on every push to main and every PR. Coverage is uploaded to Codecov.
- Request signature verification: The built-in receivers validate
x-slack-signatureandx-slack-request-timestampon every incoming HTTP request usingtsscmpfor timing-safe comparison. Never disablesignatureVerificationin production. - Tokens and secrets:
SLACK_SIGNING_SECRET,SLACK_BOT_TOKEN, andSLACK_APP_TOKENmust come from environment variables. Never hardcode or commit secrets. - Authorization middleware: Verifies tokens and injects an authorized
WebClientinto the listener context. Do not bypass authorization. - Tests: Always use mock/stub values for tokens and secrets. Never use real credentials in tests.