Skip to content

How to Make a Discord Bot? First Slash Command with discord.js

From creating an application in the Developer Portal to hiding the token with .env, selecting intents, and registering commands via REST, write your first slash command working with discord.js 14.27.

VeyroBot team

This article was translated automatically from the Turkish original. Read the Turkish original

How to Make a Discord Bot? First Slash Command with discord.js
In this article
  1. What you need and versions
  2. Creating an application and bot in the Developer Portal
  3. Keeping the token secure
  4. Inviting the bot to the server
  5. Setting up the project
  6. Intents: request only what you need
  7. Defining and registering the ping command
  8. Running the bot and responding to the command
  9. If the command is not showing up or throwing an error
  10. Next steps

In Short

To make a Discord bot, you create an application in the Developer Portal, keep your bot token hidden in a .env file, and add the bot to your server with the bot and applications.commands scopes. Next, you install Node.js and discord.js, write a command definition, register it via REST, and reply to it in the interactionCreate event. The code in this guide is compatible with discord.js 14.27.0, which is the latest version as of October 9, 2026.

This article is a beginner's guide that ends with a single working command for someone who has never written a bot before. By the end, you will have a ping command that gives a "Pong!" response visible only to you when typed in your server. The guide follows the official discord.js guide and Discord developer documentation. VeyroBot is also a bot written with discord.js; the building blocks described here are the foundation for large bots as well.

What you need and versions

  • Node.js: discord.js 14 requires at least Node.js 18. However, Node.js 18 is now out of support. According to the Node.js releases page, as of October 9, 2026, the LTS versions are 24 and 22. 24 LTS is a sensible choice for a new project.
  • discord.js: As of October 9, 2026, the latest version is 14.27.0.
  • A test server: An empty server where you are an administrator, set up for experiments, is most convenient.
  • A code editor and terminal.

To verify the installation, print the version in your terminal:

node --version

Creating an application and bot in the Developer Portal

  1. Click the Create App button in the Developer Portal, enter a name, and create it.
  2. Copy the Application ID value from the General Information page. This will be used as CLIENT_ID in the code.
  3. Switch to the Bot page from the left menu. Generate a new token using Reset Token under the Token section and save it securely right away. According to Discord's documentation, the token is not shown again unless regenerated.
  4. Privileged intent switches (Presence, Server Members, Message Content) remain on the same page. You do not need to enable any of them for the command in this guide.

Keeping the token secure

The token is your bot's password. The Discord documentation explicitly states that the token is "extremely sensitive," should never be shared, and should never be added to version control. Anyone who obtains your token can act on behalf of your bot with all the permissions the bot possesses.

Therefore, do not hardcode the token; put it in a .env file in your project folder:

DISCORD_TOKEN=your-token-here
CLIENT_ID=your-application-id-here
GUILD_ID=your-test-server-id-here

If you are using Git, add the .env line to your .gitignore file. If a token is accidentally leaked somewhere, regenerate it from the Bot page; the old token will become invalid.

To get the server ID (GUILD_ID), enable Developer Mode under User Settings > Advanced in Discord, then right-click your server icon and copy the server ID. The steps are explained in Discord's finding your ID article.

Inviting the bot to the server

Go to the Installation page in the Developer Portal. Add the bot and applications.commands scopes to the default install settings for Guild Install. When bot is selected, a list of permissions for the bot opens up. Open the generated link in your browser and select your test server.

According to Discord's OAuth2 documentation, the applications.commands scope is already included in the bot scope. Selecting both together does no harm and makes the intent explicit.

Apply the principle of least privilege in permissions: the bot should only receive the permissions required for its job. No additional server permission is required to respond to a slash command. Granting administrator permission might seem like a convenience, but if the token leaks, it exposes the entire server. For details, you can check the Discord bot permissions: Should I grant Administrator? article.

Setting up the project

Open an empty folder and run the following commands inside it:

npm init -y
npm install discord.js

This guide does not use any additional packages. We will load the .env file using Node.js's built-in --env-file option. According to the Node.js documentation, this option arrived in version 20.6.0 and has not been experimental since versions 24.10.0 and 22.21.0.

Intents: request only what you need

Intents determine which events the bot will receive from the gateway. Requesting fewer intents both cuts unnecessary data flow and saves you from verification processes. For the ping command, the Guilds intent is sufficient; discord.js caches server and channel information with it.

According to the Gateway documentation, there are currently three privileged intents: GUILD_PRESENCES, GUILD_MEMBERS, and MESSAGE_CONTENT. You must enable them in the Developer Portal before requesting them in code. Bots that require verification, i.e., bots in 100 or more servers, need separate approval for these intents. Slash commands do not need message content; therefore, most new bots can run without the Message Content intent.

If you request a privileged intent in code that you haven't enabled in the Portal, the bot cannot log in. The solution to this error is explained in the Fixing the Discord.js v14 "Used Disallowed Intents" Error article.

Defining and registering the ping command

You need to notify Discord of the command's name and description in advance. This registration is a one-time operation separate from running the bot. Create a file named deploy-commands.js in the folder:

const { REST, Routes, SlashCommandBuilder } = require('discord.js');

const commands = [
  new SlashCommandBuilder()
    .setName('ping')
    .setDescription('Tests whether the bot is responding'),
].map((command) => command.toJSON());

const rest = new REST().setToken(process.env.DISCORD_TOKEN);

(async () => {
  try {
    const registered = await rest.put(
      Routes.applicationGuildCommands(process.env.CLIENT_ID, process.env.GUILD_ID),
      { body: commands },
    );
    console.log(`Successfully registered ${registered.length} commands.`);
  } catch (error) {
    console.error(error);
  }
})();

Run it:

node --env-file=.env deploy-commands.js

rest.put replaces all commands on that server with the list you sent. A command you remove from the list is deleted on the next registration.

Guild-specific command or global command?

TypeRouteWhere it appears
Guild-specificRoutes.applicationGuildCommands(clientId, guildId)Only in the specified server
GlobalRoutes.applicationCommands(clientId)In all servers where the application is added and in DMs by default

The discord.js guide recommends guild-specific registration during development; once the command is ready, you switch to global registration. Discord's command documentation states there is a limit of 200 command creations per guild per day. Therefore, run the registration script only when the command definition changes, not every time the bot starts up. According to the same document, an application can have at most 100 global slash commands.

Running the bot and responding to the command

Now write the bot itself. Add the index.js file to the folder:

const { Client, Events, GatewayIntentBits, MessageFlags } = require('discord.js');

const client = new Client({ intents: [GatewayIntentBits.Guilds] });

client.once(Events.ClientReady, (readyClient) => {
  console.log(`Logged in as: ${readyClient.user.tag}`);
});

client.on(Events.InteractionCreate, async (interaction) => {
  if (interaction.isChatInputCommand() && interaction.commandName === 'ping') {
    await interaction.reply({
      content: `Pong! Gateway latency: ${client.ws.ping} ms`,
      flags: MessageFlags.Ephemeral,
    });
  }
});

client.login(process.env.DISCORD_TOKEN);
node --env-file=.env index.js

When you see the "Logged in as" line in the terminal, type the ping command in your test server. The response will be visible only to you.

Three details in the code are related to the current discord.js version:

  • Ephemeral response: flags: MessageFlags.Ephemeral is used for responses that only the command issuer can see. The old ephemeral: true option is deprecated and discord.js 14.27.0 prints a warning for it.
  • Ready event: The Events.ClientReady value corresponds to the clientReady event. The old ready name still works, but the library gives a warning stating that this name will be removed in version 15.
  • Fetching the response: If you need the response message you sent, use the withResponse: true option or the interaction.fetchReply() method instead of the deprecated fetchReply: true.

According to the Interaction documentation, you must provide the initial response within 3 seconds. If your task will take longer, first send a waiting signal with interaction.deferReply(). The interaction token is valid for 15 minutes; you can send additional messages during this time.

If the command is not showing up or throwing an error

The command is completely missing from the list

Check that the registration script printed "Successfully registered 1 commands." Make sure the GUILD_ID value belongs to the server the bot was added to. If the bot was added without the applications.commands scope, command registration will be rejected; re-add the bot using the installation link.

"The application did not respond" warning

This means Discord did not receive a response within 3 seconds. Check that the bot is online, there are no errors in the terminal, and the commandName comparison matches the name in the registration exactly.

The bot is not logging in

The "An invalid token was provided." error indicates that the token is incorrect or has been reset. Check that the token is written without quotes and spaces in the .env file. If you get a "Used disallowed intents" error, a privileged intent you requested is disabled in the Portal.

The command works but another operation gives a permission error

In operations like assigning roles or deleting messages, both the bot's permission and its role hierarchy matter. These situations are covered in the Discord bot permission errors article.

Next steps

A single-file bot becomes harder to read as the number of commands grows. The discord.js guide explains how to split commands into separate files and load them with a command handler. Option-based commands, buttons, and error handling are also covered in subsequent sections of the guide. Before opening the bot to everyone, double-check that the token is not exposed anywhere and that the bot is only requesting the permissions it needs.

Last updated: October 9, 2026. Sources: discordjs.guide, docs.discord.com (Getting Started, Application Commands, Receiving and Responding, OAuth2, Gateway), nodejs.org releases page and CLI documentation, discord.js GitHub releases page. Information regarding deprecated options was verified from warnings in the discord.js 14.27.0 source code.

Add the bot to your server Free · ~30s