Suppose you are trying to create a Discord bot and slash commands. You are using Node.js and TypeScript and you stumble upon the Discord.js library. I mean, how could you miss it? It shows up in search results before even Discord’s official documentation so you’d be forgiven to think that it is official. And you might look at some of the code and examples and think, “Wow, this sure is… a lot of words. And there are for loops?” And maybe you’d think that this is just how Discord engineers want developers to do things. And it might make you think that this is really fucking complicated.

Surprise! Discord.js is not an official Discord library. And where slash commands are concerned, the code makes it significantly more complicated than it needs to be. It’s so complicated that I’d argue it’s doing a disservice to Discord and the talented Discord.js developers who maintain the package.

So without further ado, if you want to make a very simple Discord slash command, it’s as simple as this:

  1. Make the API call to create the slash command

In Discord.js, you’d use their SlashCommandBuilder class to configure it. You can do that and just console.log the output as JSON, console.log(command.toJSON()). It’ll give you the payload of the API command you need.

BOT_TOKEN='replace_me_with_bot_token'
CLIENT_ID='replace_me_with_client_id'
curl -X POST \
-H 'Content-Type: application/json' \
-H "Authorization: Bot $BOT_TOKEN" \
-d '{"name":"hello","description":"Greet a person","options":[{"name":"name","description":"The name of the person","type":3,"required":true}]}' \
"https://discord.com/api/v8/applications/$CLIENT_ID/commands"

That’s an example from https://docs.deno.com/deploy/tutorials/discord-slash/#step-2%3A-register-slash-command-with-discord-app

  1. Setup the handler for the slash command

The Discord.js library has a lot of code about dynamically loading slash commands and using an array or a Collection so you can do dynamic lookups using names. None of that is necessary at all. You’d do that if you have a lot of slash commands and they’re changing so frequently that you can’t be bothered to manually maintain a list of commands to support. Tutorial docs do not need that.

The simple version is just a switch statement. Assuming you are using the Discord.js library with an instance of client

  discordClient.on(Events.InteractionCreate, async (interaction) => {
    if (!interaction.isChatInputCommand()) {
      logger.info('Command is not a chat input command');
      return;
    }

    switch (interaction.commandName) {
      case 'your-command-here': {
        const { options } = interaction;
        const code = options.getString('code');
        if (!code) {
          logger.info('No code provided');
          await interaction.reply({ content: 'No code provided', ephemeral: true });
          return;
        }

        const { id: discordUid, username: discordUsername } = interaction.user;
        await interaction.reply({ content: 'Successfully verified!', ephemeral: true });

        break;
      }
    }
  });

That’s it! Just like handling every webhook in existence! And if you want type safety or you want to handle it dynamically, there are easy ways to do that, too.

I don’t like speaking ill of open source projects. Discord.js is a powerful library and I appreciate the care that went into it. I know that the docs I’m complaining about come from someone trying to be helpful and offering solutions that they think will help. But the complexity of the docs turned me off to the whole process and ultimately cast both the library and Discord itself in a bad light until I realized the complexity was coming from implementation details.