Middlewares
Middlewares in CommandKit allow you to execute code before and after command execution. This is incredibly powerful for implementing features like logging, authentication, permission checks, or any other cross-cutting concerns for your Discord bot.
CommandKit supports three different types of middleware files, each serving different scoping purposes for your commands.
Basic middleware structure
All middleware files follow the same export pattern. You can export
beforeExecute
and afterExecute
functions that will run at their
respective times during command execution.
import { MiddlewareContext } from 'commandkit';
export function beforeExecute(ctx: MiddlewareContext) {
// This function will be executed before the command is executed
console.log(`User ${ctx.interaction.user.id} is about to execute a command`);
}
export function afterExecute(ctx: MiddlewareContext) {
// This function will be executed after the command is executed
console.log(
`Command execution completed for user ${ctx.interaction.user.id}`,
);
}
Stop command execution
You can stop a command from running by calling stopMiddlewares()
in
the beforeExecute
function.
import type { MiddlewareContext } from 'commandkit';
export function beforeExecute(ctx: MiddlewareContext) {
if (ctx.interaction.user.id !== '1234567890') {
// Conditionally stop command execution
console.log(`${ctx.commandName} will not be executed!`);
stopMiddlewares();
}
// Continue with command execution
console.log(`${ctx.commandName} will be executed!`);
}
You can also use stopMiddlewares()
inside a command function to stop
any afterExecute
middlewares from running.
In addition, you can also use stopMiddlewares()
inside any
afterExecute
middleware function to stop any remaining middlewares
from running.
Calling stopMiddlewares()
in a try/catch block may lead to
unexpected behavior.
If you still want to use stopMiddlewares()
in a try/catch block, you
can use the isErrorType
function to check if the error is an
instance of the CommandKitErrorCodes.StopMiddlewares
error.
import type { MiddlewareContext } from 'commandkit';
export function beforeExecute(ctx: MiddlewareContext) {
try {
// code that may throw an error
stopMiddlewares(); // conditionally stop the middleware chain
} catch (error) {
if (isErrorType(error, CommandKitErrorCodes.StopMiddlewares)) {
// if stopMiddlewares() is called in the try block, throw it so CommandKit can stop the middleware chain
throw error;
}
// this means that the code threw the error, and stopMiddlewares() was not called
// the rest of the middlewares will be executed as normal
console.error(error);
}
}
Middleware types
Directory-scoped middleware
Create a +middleware.ts
file in any commands directory to apply
middleware to all commands within that directory and its
subdirectories.
import type { MiddlewareContext } from 'commandkit';
export function beforeExecute(ctx: MiddlewareContext) {
// This middleware will run before any moderation command
if (!ctx.interaction.member.permissions.has('KickMembers')) {
throw new Error('You need moderation permissions to use this command');
}
}
Command-specific middleware
For command-specific middleware, create a file named
+<command-name>.middleware.ts
where <command-name>
matches your
command file name.
import type { MiddlewareContext } from 'commandkit';
export function beforeExecute(ctx: MiddlewareContext) {
// This middleware only runs before the ban command
console.log('Ban command is about to be executed');
}
Global middleware
Create a +global-middleware.ts
file in your commands directory to
apply middleware to every command in your entire Discord bot.
import type { MiddlewareContext } from 'commandkit';
export function beforeExecute(ctx: MiddlewareContext) {
// This middleware runs before ANY command in your bot
console.log(
`Command executed by ${ctx.interaction.user.tag} in ${ctx.interaction.guild?.name || 'DMs'}`,
);
}
Middleware execution (both before and after) follows a hierarchy: command-scoped middlewares run first, then directory-scoped middlewares, and finally global middlewares.