Slash command starter (JavaScript)

Registers /ping and /hello and answers them. A clean base for your own commands.

Node.js 22

Environment variables you'll need

KeyWhat it is
DISCORD_TOKENYour bot's token from the Discord Developer Portal. Paste it on the Secrets tab; we store it encrypted.
GUILD_IDOptional. Register the commands in this server only, which is instant

Create an account to use it

Files

README.md

# Slash command starter

A JavaScript bot built on discord.js. Your bot token is set on the Secrets tab as
DISCORD_TOKEN and reaches the bot as an environment variable.

## Commands

- `/ping` replies with the bot's latency.
- `/hello` greets you.
- Without GUILD_ID the commands are registered for every server, which can take up to an hour to appear.

## Setup

1. No privileged gateway intents are needed. Leave Message Content, Server Members
   and Presence off in the Discord Developer Portal.
2. Invite the bot with the `bot` and `applications.commands` scopes and these
   permissions: Send Messages.
3. Set these values on the Secrets tab. The ones marked optional can stay empty:

- `GUILD_ID`: Optional. Register the commands in this server only, which is instant.

4. Press Deploy. The console shows a line with "Logged in as" once the bot is online.

index.js

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

const TOKEN = process.env.DISCORD_TOKEN;
const GUILD_ID = (process.env.GUILD_ID || '').trim();

const commands = [
  new SlashCommandBuilder().setName('ping').setDescription('Check that the bot is alive'),
  new SlashCommandBuilder().setName('hello').setDescription('Say hello'),
].map((c) => c.toJSON());

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

async function registerCommands(appId) {
  const rest = new REST().setToken(TOKEN);
  // Guild commands show up instantly; global commands can take a little longer.
  const route = GUILD_ID
    ? Routes.applicationGuildCommands(appId, GUILD_ID)
    : Routes.applicationCommands(appId);
  await rest.put(route, { body: commands });
  console.log(`Registered ${commands.length} commands ${GUILD_ID ? `in guild ${GUILD_ID}` : 'globally'}`);
}

client.once(Events.ClientReady, async (c) => {
  console.log(`Logged in as ${c.user.tag} (ready)`);
  try {
    await registerCommands(c.user.id);
  } catch (err) {
    console.error('Could not register commands:', err);
  }
});

client.on(Events.InteractionCreate, async (interaction) => {
  if (!interaction.isChatInputCommand()) return;
  try {
    if (interaction.commandName === 'ping') {
      await interaction.reply(`Pong! ${Math.round(client.ws.ping)} ms`);
    } else if (interaction.commandName === 'hello') {
      await interaction.reply(`Hello, ${interaction.user}!`);
    }
  } catch (err) {
    console.error('Command failed:', err);
    try {
      if (interaction.deferred || interaction.replied) await interaction.followUp({ content: 'Something went wrong.', flags: MessageFlags.Ephemeral });
      else await interaction.reply({ content: 'Something went wrong.', flags: MessageFlags.Ephemeral });
    } catch { /* nothing else to do */ }
  }
});

client.on(Events.Error, (err) => console.error('Client error:', err));
process.on('unhandledRejection', (err) => console.error('Unhandled rejection:', err));

client.login(TOKEN);

package.json

{
  "name": "slash-starter-js",
  "private": true,
  "main": "index.js",
  "engines": { "node": ">=22" },
  "dependencies": { "discord.js": "^14.0.0" }
}