Using Slash Commands in discord.js: Interaction Handlers vs Message Parsing
Learn why Discord.js bots should use slash commands and interaction handlers instead of parsing message content, with a concrete command‑collection example and guidance on deferred replies.
30 Jul 2025, 19:18 UTC

The Problem with Parsing Message Content
In the early days of Discord bot development, commands were simple: you listened for messageCreate events, checked if the string started with a prefix, and manually split the text to find arguments. Today, that approach is a liability. Discord has moved 'Message Content' behind a privileged gateway intent, meaning your bot cannot even see what users are typing unless you are verified and have gone through a manual review process.
Why Slash Commands Shift the Work to Discord
When a user executes a slash command, Discord sends an interactionCreate event instead of a raw message. In discord.js this appears as a ChatInputCommandInteraction object. The interaction provides typed accessors such as getString, getInteger, and getUser, removing the need for manual parsing and validation.
Furthermore, you can declare command metadata directly on the SlashCommandBuilder. For example, setDefaultMemberPermissions lets Discord hide the command from users who lack the required permission, eliminating boiler‑plate permission checks in your code.
Building a Command Collection in discord.js v14
To keep the codebase scalable, store each command’s definition and handler in a separate file and register them in a Collection. The bot then dispatches from a single interactionCreate listener.
// commands/ping.js
const { SlashCommandBuilder } = require('discord.js');
module.exports = {
data: new SlashCommandBuilder()
.setName('ping')
.setDescription('Replies with the bot latency')
.addIntegerOption(option =>
option.setName('repeat')
.setDescription('How many times to ping')
.setMinValue(1)
.setMaxValue(5)),
async execute(interaction) {
const repeat = interaction.options.getInteger('repeat') ?? 1;
await interaction.reply(`Pong! ${interaction.client.ws.ping}ms`);
},
};
// main.js (simplified)
const { Client, GatewayIntentBits, Collection } = require('discord.js');
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
client.commands = new Collection();
// …load command files into client.commands here…
client.on('interactionCreate', async interaction => {
if (!interaction.isChatInputCommand()) return;
const command = client.commands.get(interaction.commandName);
if (!command) return;
try {
await command.execute(interaction);
} catch (err) {
console.error(err);
if (interaction.replied) {
await interaction.followReply({ content: 'There was an error!', ephemeral: true });
} else {
await interaction.reply({ content: 'There was an error!', ephemeral: true });
}
}
});
Handling Long‑Running Work with Deferred Replies
Discord expects an initial response within three seconds. For tasks that may exceed this limit—such as a database query or an external API call—you must call await interaction.deferReply(). This puts the interaction in a “thinking” state and extends the deadline. Once the work finishes, use await interaction.editReply(content) to replace the placeholder with the final result.
async execute(interaction) {
await interaction.deferReply();
const data = await slowDatabaseQuery(); // placeholder
await interaction.editReply(`Finished: ${data}`);
}
Trade‑offs and Practical Tips
- Slash commands remove the need for the privileged
Message Contentintent, simplifying verification and reducing privacy concerns. - Guild‑scoped command registration propagates almost instantly, making it ideal for development; global commands can take up to an hour to update.
- Interaction tokens expire after roughly 15 minutes, so follow‑up edits must happen within that window or you need to fetch a new token via a new interaction.
- If you genuinely need to read raw message text (e.g., for a moderation bot), you must request the
Message Contentintent and undergo Discord’s review process.
Adopting slash commands and interaction handlers makes your discord.js bot more reliable, easier to maintain, and aligned with Discord’s current best practices. Start by auditing your intents, register a few guild‑scoped slash commands, and replace any messageCreate‑based parsers with the typed interaction pattern shown above.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.