0
# Built-in Middleware
1
2
Slack Bolt provides built-in middleware functions for filtering events, matching patterns, and processing requests. These middleware functions help route requests to appropriate handlers and provide common functionality.
3
4
## Capabilities
5
6
### Event Filtering Middleware
7
8
Filter incoming requests to specific event types for focused request processing.
9
10
```typescript { .api }
11
/**
12
* Middleware that filters out any event that isn't an action
13
* Only allows interactive component actions to pass through
14
*/
15
const onlyActions: Middleware<AnyMiddlewareArgs>;
16
17
/**
18
* Middleware that filters out any event that isn't a command
19
* Only allows slash commands to pass through
20
*/
21
const onlyCommands: Middleware<AnyMiddlewareArgs>;
22
23
/**
24
* Middleware that filters out any event that isn't a Slack event
25
* Only allows Events API events to pass through
26
*/
27
const onlyEvents: Middleware<AnyMiddlewareArgs>;
28
29
/**
30
* Middleware that filters out any event that isn't an options request
31
* Only allows external select menu option requests to pass through
32
*/
33
const onlyOptions: Middleware<AnyMiddlewareArgs>;
34
35
/**
36
* Middleware that filters out any event that isn't a shortcut
37
* Only allows global and message shortcuts to pass through
38
*/
39
const onlyShortcuts: Middleware<AnyMiddlewareArgs>;
40
41
/**
42
* Middleware that filters out any event that isn't a view action
43
* Only allows modal view submissions and closures to pass through
44
*/
45
const onlyViewActions: Middleware<AnyMiddlewareArgs>;
46
```
47
48
### Utility Middleware
49
50
General-purpose middleware for common functionality and request processing.
51
52
```typescript { .api }
53
/**
54
* Middleware that automatically acknowledges requests
55
* Calls ack() immediately when the middleware is invoked
56
*/
57
const autoAcknowledge: Middleware<AnyMiddlewareArgs>;
58
59
/**
60
* Middleware that ignores events from the bot itself
61
* Filters out messages and events where the user is the bot
62
*/
63
const ignoreSelf: Middleware<AnyMiddlewareArgs>;
64
65
/**
66
* Middleware that only allows direct mentions of the bot
67
* Useful for filtering message events to only direct mentions
68
*/
69
const directMention: Middleware<SlackEventMiddlewareArgs<'message'>>;
70
```
71
72
**Usage Examples:**
73
74
```typescript
75
import { App, onlyActions, onlyCommands } from "@slack/bolt";
76
77
const app = new App({
78
token: process.env.SLACK_BOT_TOKEN,
79
signingSecret: process.env.SLACK_SIGNING_SECRET
80
});
81
82
// Use filtering middleware globally
83
app.use(onlyActions);
84
85
// Or use in specific listeners
86
app.use(onlyCommands, async ({ command, ack, respond }) => {
87
await ack();
88
await respond(`Received command: ${command.command}`);
89
});
90
```
91
92
### Pattern Matching Middleware
93
94
Match requests against specific patterns for conditional processing.
95
96
```typescript { .api }
97
/**
98
* Middleware that matches message text against a pattern
99
* @param pattern - String or RegExp pattern to match against message text
100
* @returns Middleware that only processes matching messages
101
*/
102
function matchMessage(
103
pattern: string | RegExp
104
): Middleware<SlackEventMiddlewareArgs<'message'>>;
105
106
/**
107
* Middleware that matches slash command names against a pattern
108
* @param pattern - String or RegExp pattern to match against command name
109
* @returns Middleware that only processes matching commands
110
*/
111
function matchCommandName(
112
pattern: string | RegExp
113
): Middleware<SlackCommandMiddlewareArgs>;
114
115
/**
116
* Middleware that matches event types against a pattern
117
* @param pattern - Event type pattern to match
118
* @returns Middleware that only processes matching event types
119
*/
120
function matchEventType(
121
pattern: EventTypePattern
122
): Middleware<SlackEventMiddlewareArgs>;
123
124
/**
125
* Middleware that matches actions against constraint criteria
126
* @param constraints - Action constraints to match against
127
* @returns Middleware that only processes matching actions
128
*/
129
function matchConstraints<T>(
130
constraints: ActionConstraints
131
): Middleware<SlackActionMiddlewareArgs>;
132
133
/**
134
* Middleware that filters messages by subtype
135
* @param subtypeValue - Message subtype to match
136
* @returns Middleware that only processes messages with matching subtype
137
*/
138
function subtype(
139
subtypeValue: string
140
): Middleware<SlackEventMiddlewareArgs<'message'>>;
141
```
142
143
**Usage Examples:**
144
145
```typescript
146
import { App, matchMessage, matchCommandName } from "@slack/bolt";
147
148
const app = new App({
149
token: process.env.SLACK_BOT_TOKEN,
150
signingSecret: process.env.SLACK_SIGNING_SECRET
151
});
152
153
// Match messages containing "hello"
154
app.use(matchMessage("hello"), async ({ message, say }) => {
155
await say(`Hello <@${message.user}>!`);
156
});
157
158
// Match messages with regex pattern
159
app.use(matchMessage(/ticket-(\d+)/), async ({ message, say, context }) => {
160
const ticketId = context.matches?.[1];
161
await say(`Looking up ticket ${ticketId}`);
162
});
163
164
// Match specific command names
165
app.use(matchCommandName("/deploy"), async ({ command, ack, respond }) => {
166
await ack();
167
await respond("Starting deployment...");
168
});
169
170
// Match multiple command patterns
171
app.use(matchCommandName(/^\/(help|info)$/), async ({ command, ack, respond }) => {
172
await ack();
173
await respond("Here's some helpful information...");
174
});
175
```
176
177
### Utility Middleware
178
179
General-purpose middleware for common functionality.
180
181
```typescript { .api }
182
/**
183
* Middleware that ignores events from the bot itself
184
* Prevents infinite loops when the bot responds to its own messages
185
*/
186
const ignoreSelf: Middleware<AnyMiddlewareArgs>;
187
188
/**
189
* Middleware that auto acknowledges the request received
190
* Automatically calls ack() if available, then continues to next middleware
191
*/
192
const autoAcknowledge: Middleware<AnyMiddlewareArgs>;
193
194
/**
195
* Middleware that filters out messages that don't start with a direct mention
196
* Only allows messages that begin with @bot_user_id
197
* @returns Middleware that only processes direct mentions
198
*/
199
const directMention: Middleware<SlackEventMiddlewareArgs<'message'>>;
200
201
/**
202
* Middleware that filters messages by subtype
203
* @param subtype - Message subtype to match (e.g., "bot_message", "file_share")
204
* @returns Middleware that only processes messages with matching subtype
205
*/
206
function subtype(subtype: string): Middleware<SlackEventMiddlewareArgs<'message'>>;
207
208
/**
209
* Type guard to check if options object contains event middleware options
210
* @param optionOrListener - Options object or listener function
211
* @returns True if input is SlackEventMiddlewareArgsOptions
212
*/
213
function isSlackEventMiddlewareArgsOptions<EventType extends string = string>(
214
optionOrListener: SlackEventMiddlewareArgsOptions | Middleware<SlackEventMiddlewareArgs<EventType>>
215
): optionOrListener is SlackEventMiddlewareArgsOptions;
216
```
217
218
**Usage Examples:**
219
220
```typescript
221
import {
222
App,
223
ignoreSelf,
224
autoAcknowledge,
225
directMention,
226
subtype
227
} from "@slack/bolt";
228
229
const app = new App({
230
token: process.env.SLACK_BOT_TOKEN,
231
signingSecret: process.env.SLACK_SIGNING_SECRET
232
});
233
234
// Add ignoreSelf middleware globally
235
app.use(ignoreSelf);
236
237
// Now the bot won't respond to its own messages
238
app.message("hello", async ({ message, say }) => {
239
await say("Hello there!"); // Won't trigger itself
240
});
241
242
// Auto-acknowledge all interactions
243
app.use(autoAcknowledge);
244
245
// Handle direct mentions only
246
app.message(directMention, async ({ message, say }) => {
247
await say(`You mentioned me, <@${message.user}>!`);
248
});
249
250
// Handle only bot messages
251
app.message(subtype("bot_message"), async ({ message, say }) => {
252
// This will only process messages from other bots
253
console.log("Received bot message:", message.text);
254
});
255
256
// Handle file share messages
257
app.message(subtype("file_share"), async ({ message, client }) => {
258
// Process file sharing events
259
console.log("File shared:", message.files);
260
});
261
262
// Custom ignore middleware example
263
app.use(async ({ context, body, next }) => {
264
// Skip if this is a bot message (alternative to ignoreSelf)
265
if (body.event?.bot_id) {
266
return;
267
}
268
await next();
269
});
270
```
271
272
### Custom Middleware Creation
273
274
Create custom middleware functions for specific application needs.
275
276
```typescript { .api }
277
/**
278
* Custom middleware function type
279
* @param args - Middleware arguments including context, logger, client, and next
280
*/
281
type Middleware<Args, CustomContext = StringIndexed> = (
282
args: Args & AllMiddlewareArgs<CustomContext>
283
) => Promise<void>;
284
285
interface AllMiddlewareArgs<CustomContext = StringIndexed> {
286
/** Request context with bot tokens and user information */
287
context: Context & CustomContext;
288
/** Logger instance for debugging and monitoring */
289
logger: Logger;
290
/** Slack Web API client for making API calls */
291
client: WebClient;
292
/** Function to call next middleware in the chain */
293
next: NextFn;
294
}
295
```
296
297
**Usage Examples:**
298
299
```typescript
300
import { App, Middleware, AnyMiddlewareArgs } from "@slack/bolt";
301
302
const app = new App({
303
token: process.env.SLACK_BOT_TOKEN,
304
signingSecret: process.env.SLACK_SIGNING_SECRET
305
});
306
307
// Custom logging middleware
308
const loggingMiddleware: Middleware<AnyMiddlewareArgs> = async ({
309
logger,
310
body,
311
next
312
}) => {
313
const startTime = Date.now();
314
315
logger.info(`Processing ${body.type || 'unknown'} event`);
316
317
await next();
318
319
const duration = Date.now() - startTime;
320
logger.info(`Completed processing in ${duration}ms`);
321
};
322
323
// Custom authorization middleware
324
const authMiddleware: Middleware<AnyMiddlewareArgs> = async ({
325
context,
326
client,
327
next
328
}) => {
329
// Add custom user data to context
330
if (context.userId) {
331
context.userData = await getUserData(context.userId);
332
}
333
334
await next();
335
};
336
337
// Rate limiting middleware
338
const rateLimitMiddleware: Middleware<AnyMiddlewareArgs> = async ({
339
context,
340
logger,
341
next
342
}) => {
343
const userId = context.userId;
344
if (!userId) {
345
await next();
346
return;
347
}
348
349
const isAllowed = await checkRateLimit(userId);
350
if (!isAllowed) {
351
logger.warn(`Rate limit exceeded for user ${userId}`);
352
return; // Don't call next(), stopping the middleware chain
353
}
354
355
await next();
356
};
357
358
// Apply custom middleware
359
app.use(loggingMiddleware);
360
app.use(authMiddleware);
361
app.use(rateLimitMiddleware);
362
363
// Conditional middleware
364
const adminOnlyMiddleware: Middleware<AnyMiddlewareArgs> = async ({
365
context,
366
client,
367
next
368
}) => {
369
const isAdmin = await checkIfAdmin(context.userId);
370
if (!isAdmin) {
371
return; // Stop processing if not admin
372
}
373
374
await next();
375
};
376
377
// Use conditional middleware for specific commands
378
app.command("/admin", adminOnlyMiddleware, async ({ command, ack, respond }) => {
379
await ack();
380
await respond("Admin command executed!");
381
});
382
```
383
384
## Middleware Processing
385
386
Understanding how middleware chains work in Slack Bolt.
387
388
```typescript { .api }
389
/**
390
* Next function type for calling the next middleware in the chain
391
*/
392
type NextFn = () => Promise<void>;
393
394
/**
395
* Middleware execution follows this pattern:
396
* 1. Global middleware (added with app.use())
397
* 2. Built-in filtering middleware (if applicable)
398
* 3. Listener-specific middleware
399
* 4. Final listener function
400
*/
401
```
402
403
**Middleware Chain Example:**
404
405
```typescript
406
const app = new App({
407
token: process.env.SLACK_BOT_TOKEN,
408
signingSecret: process.env.SLACK_SIGNING_SECRET
409
});
410
411
// 1. Global middleware - runs for all requests
412
app.use(async ({ logger, next }) => {
413
logger.info("Global middleware start");
414
await next();
415
logger.info("Global middleware end");
416
});
417
418
// 2. Event listener with middleware chain
419
app.message(
420
// Built-in pattern matching middleware
421
"hello",
422
// Custom middleware for this listener
423
async ({ logger, next }) => {
424
logger.info("Message middleware start");
425
await next();
426
logger.info("Message middleware end");
427
},
428
// Final listener function
429
async ({ message, say, logger }) => {
430
logger.info("Processing message");
431
await say(`Hello <@${message.user}>!`);
432
}
433
);
434
435
// Execution order for "hello" message:
436
// 1. Global middleware start
437
// 2. Built-in message matching
438
// 3. Message middleware start
439
// 4. Processing message
440
// 5. Message middleware end
441
// 6. Global middleware end
442
```
443
444
## Constraint Types
445
446
Type definitions for matching constraints used in middleware.
447
448
```typescript { .api }
449
interface ActionConstraints {
450
type?: string;
451
action_id?: string | RegExp;
452
block_id?: string | RegExp;
453
callback_id?: string | RegExp;
454
}
455
456
interface OptionsConstraints {
457
action_id?: string | RegExp;
458
block_id?: string | RegExp;
459
callback_id?: string | RegExp;
460
}
461
462
interface ShortcutConstraints {
463
type?: 'shortcut' | 'message_action';
464
callback_id?: string | RegExp;
465
}
466
467
interface ViewConstraints {
468
callback_id?: string | RegExp;
469
type?: 'view_submission' | 'view_closed';
470
}
471
472
type EventTypePattern = string | RegExp;
473
```