Add middleware to your commands for permissions, logging, cooldowns, and more
Middlewares in CommandKit allow you to execute code before and after command execution. They’re perfect for implementing permissions checks, logging, rate limiting, and other cross-cutting concerns.
Middlewares are defined by exporting beforeExecute and/or afterExecute functions from files with specific naming patterns:
import { MiddlewareContext, stopMiddlewares } from 'commandkit';export function beforeExecute(ctx: MiddlewareContext) { console.log(`${ctx.commandName} will be executed`);}export function afterExecute(ctx: MiddlewareContext) { console.log(`${ctx.commandName} has been executed`);}
CommandKit supports three levels of middleware scope:
Global middleware
Directory middleware
Command middleware
Applies to all commands. Create a file named +global-middleware.ts in your commands directory:
src/app/commands/+global-middleware.ts
import { Logger, MiddlewareContext } from 'commandkit';export function beforeExecute(ctx: MiddlewareContext) { Logger.info(`Global middleware: ${ctx.commandName} will be executed`);}export function afterExecute(ctx: MiddlewareContext) { Logger.info(`Global middleware: ${ctx.commandName} has been executed`);}
Applies to all commands in a directory and its subdirectories. Create a file named +middleware.ts in any directory:
src/app/commands/(admin)/+middleware.ts
import { Logger, MiddlewareContext, stopMiddlewares } from 'commandkit';export function beforeExecute(ctx: MiddlewareContext) { Logger.info(`Directory middleware: ${ctx.commandName} will be executed`);}export function afterExecute(ctx: MiddlewareContext) { Logger.info(`Directory middleware: ${ctx.commandName} has been executed`);}
Applies only to a specific command. Create a file named +{command}.middleware.ts:
src/app/commands/(general)/+ping.middleware.ts
import { Logger, MiddlewareContext } from 'commandkit';export function beforeExecute(ctx: MiddlewareContext) { Logger.info(`Command-scoped middleware: ${ctx.commandName} will be executed`); ctx.store.set('startTime', Date.now());}export function afterExecute(ctx: MiddlewareContext) { const duration = Date.now() - ctx.store.get('startTime'); Logger.info(`Command took ${duration}ms`);}
Use stopMiddlewares() to prevent further middleware execution and optionally skip command execution:
import { MiddlewareContext, stopMiddlewares } from 'commandkit';export function beforeExecute(ctx: MiddlewareContext) { const user = ctx.isInteraction() ? ctx.interaction.user : ctx.message.author; if (!hasPermission(user)) { if (ctx.isChatInputCommand()) { ctx.interaction.reply('You do not have permission!'); } // Stop all remaining middlewares and skip command execution stopMiddlewares(); }}
When stopMiddlewares() is called in beforeExecute, the command will not execute and no afterExecute middlewares will run.
import { Logger, MiddlewareContext, stopMiddlewares } from 'commandkit';import { MessageFlags } from 'discord.js';export function beforeExecute(ctx: MiddlewareContext) { const user = ctx.isInteraction() ? ctx.interaction.user : ctx.message.author; // Check if user is an admin if (user.id !== 'ADMIN_USER_ID') { if (ctx.isChatInputCommand()) { ctx.interaction.reply({ content: 'You are not allowed to use this command.', flags: MessageFlags.Ephemeral, }); } else { ctx.message.reply('You are not allowed to use this command.'); } stopMiddlewares(); }}
import { MiddlewareContext, stopMiddlewares } from 'commandkit';const MAINTENANCE_MODE = process.env.MAINTENANCE_MODE === 'true';const ADMIN_IDS = ['123456789', '987654321'];export function beforeExecute(ctx: MiddlewareContext) { if (!MAINTENANCE_MODE) return; const user = ctx.isInteraction() ? ctx.interaction.user : ctx.message.author; // Allow admins to use commands during maintenance if (ADMIN_IDS.includes(user.id)) return; if (ctx.isChatInputCommand()) { ctx.interaction.reply({ content: '🔧 Bot is currently under maintenance. Please try again later.', ephemeral: true, }); } stopMiddlewares();}