How to Create, Register, and Handle a Slash Command in Discord.js v14
A step‑by‑step guide to defining a slash command, registering it with Discord’s REST API, and handling the interaction in a Discord.js v14 bot. Includes prerequisites, code snippets, verification checks, and troubleshooting tips.
18 Jun 2026, 10:20 UTC

Desired Outcome
Deploy a single slash command that appears instantly in a guild, registers correctly with Discord’s REST API, and responds to user interactions with a simple message.
Prerequisites
- Node.js 16.9.0 or newer (recommended 18.x LTS).
- discord.js v14 installed via npm.
- A bot token with the
applications.commandspermission. - Valid guild ID for immediate testing (global commands take up to an hour).
- Environment variables or a .env file storing
DISCORD_TOKENandGUILD_ID.
Step‑by‑Step Procedure
1. Create the Command JSON
// commands/ping.js
module.exports = {
name: 'ping',
description: 'Replies with Pong!',
// Optional: add options here
};
2. Bulk‑Register the Command
// register.js
require('dotenv').config();
const { REST } = require('@discordjs/rest');
const { Routes } = require('discord-api-types/v10');
const fs = require('fs');
const token = process.env.DISCORD_TOKEN;
const clientId = process.env.CLIENT_ID; // from Developer Portal
const guildId = process.env.GUILD_ID;
const commandFiles = fs.readdirSync('./commands').filter(file => file.endsWith('.js'));
const commands = commandFiles.map(file => require(`./commands/${file}`));
const rest = new REST({ version: '10' }).setToken(token);
(async () => {
try {
console.log('Started refreshing application (/) commands.');
await rest.put(
Routes.applicationGuildCommands(clientId, guildId),
{ body: commands }
);
console.log('Successfully reloaded application (/) commands.');
} catch (error) {
console.error(error);
}
})();
Run node register.js from the project root. The command should appear immediately in the guild chat.
3. Handle the Interaction in the Bot
// bot.js
require('dotenv').config();
const { Client, GatewayIntentBits } = require('discord.js');
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
client.once('ready', () => {
console.log(`Logged in as ${client.user.tag}`);
});
client.on('interactionCreate', async interaction => {
if (!interaction.isChatInputCommand()) return;
if (interaction.commandName === 'ping') {
await interaction.reply('Pong!');
}
});
client.login(process.env.DISCORD_TOKEN);
Start the bot with node bot.js. Type /ping in the guild and you should see a reply.
Expected Checks
- In the Developer Portal, the command appears under the Guilds > Guild Name > Commands tab.
- In Discord, typing
/showspingin the autocomplete list. - The bot’s console logs the
interactionCreateevent withcommandName: ping. - When you invoke
/ping, the bot replies withPong!.
Recovery & Troubleshooting
- Command not showing up? Verify
CLIENT_IDandGUILD_IDare correct. Re‑runnode register.jsand check for errors. - Rate‑limit 429 error? Bulk register fewer than 200 commands or space out requests.
- Bot cannot register? Ensure the bot has the
applications.commandspermission in the target guild. - Interaction handler not triggered? Confirm the bot is connected to the same guild and that the command name matches exactly.
- Use
client.application.commands.fetch()in a script to programmatically confirm the command exists on Discord’s side.
Limitations
- Global commands (registered via
Routes.applicationCommands) can take up to an hour to propagate; use guild commands for quick iteration. - Discord’s API may change; keep
@discordjs/restanddiscord-api-typesup to date. - Storing tokens in plain text is insecure; use environment variables or secret management services.
Practical Verification
After registration, run:
curl -H "Authorization: Bot <token>" \
https://discord.com/api/v10/applications/<appId>/guilds/<guildId>/commands
Inspect the JSON response for your command’s name and description to confirm successful registration.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.