Discord Bots

How to Build a Discord Bot with Node.js and discord.js

Tech Setup5 min read
TS

Tech Setup

Reviewed July 21, 2026

How to Build a Discord Bot with Node.js and discord.js

Building a custom Discord bot is a rite of passage for many developers. It provides a practical playground for learning asynchronous programming, event-driven architecture, and interacting with REST APIs.

In this guide, we will walk through the process of building a production-ready Discord bot using Node.js and the industry-standard library, discord.js.

Prerequisites

Before diving into the code, ensure you have the following installed on your machine:

  • Node.js (LTS version): Recommended v18 or higher.
  • npm: Usually installed with Node.js.
  • A Discord Account: To create and manage your bot application.
  • Code Editor: VS Code is recommended.

Step 1: Create a Discord Application

Discord’s API requires an "Application" to act as the container for your bot.

  1. Navigate to the Discord Developer Portal.
  2. Click the New Application button and give it a descriptive name.
  3. In the left sidebar, click Bot.
  4. Under the Privileged Gateway Intents section, toggle the Message Content Intent to "On." This is crucial if you want your bot to read the messages sent in channels.
  5. Save your changes.
  6. Click Reset Token (or Copy Token) and store this string securely. Never share this token publicly.

Step 2: Initialize Your Project

Create a new directory for your project and initialize a Node.js project:

mkdir my-discord-bot
cd my-discord-bot
npm init -y

Next, install discord.js. As of this writing, we are targeting v14+:

npm install discord.js dotenv

Note: We installed dotenv to manage our environment variables, preventing the hardcoding of your secret bot token.

Step 3: Configure Environment Variables

Create a file named .env in the root of your project folder. This is where you will store your sensitive credentials.

DISCORD_TOKEN=your_bot_token_here
CLIENT_ID=your_application_id_here

To find your CLIENT_ID, go back to the Developer Portal under General Information.

Step 4: Structuring the Bot

We will create a simple, modular entry point. Create a file named index.js.

First, we need to import the required classes and initialize the client:

require('dotenv').config();
const { Client, GatewayIntentBits } = require('discord.js');

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

client.once('ready', () => {
    console.log(`Ready! Logged in as ${client.user.tag}`);
});

client.login(process.env.DISCORD_TOKEN);

Understanding Intents

Intents are a way for your bot to declare which events it needs to receive from Discord.

  • GatewayIntentBits.Guilds: Necessary for basic guild interactions.
  • GatewayIntentBits.GuildMessages: Allows the bot to receive messages.
  • GatewayIntentBits.MessageContent: Necessary to read the text inside messages.

Step 5: Handling Commands

The modern way to interact with Discord bots is via Slash Commands. Unlike old "prefix-based" bots (like !ping), slash commands provide a native UI in Discord.

Deploying Commands

Discord requires you to register commands with their API. Create a separate file called deploy-commands.js:

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

const commands = [
    {
        name: 'ping',
        description: 'Replies with Pong!',
    },
];

const rest = new REST({ version: '10' }).setToken(process.env.DISCORD_TOKEN);

(async () => {
    try {
        console.log('Started refreshing application (/) commands.');
        await rest.put(
            Routes.applicationCommands(process.env.CLIENT_ID),
            { body: commands },
        );
        console.log('Successfully reloaded application (/) commands.');
    } catch (error) {
        console.error(error);
    }
})();

Run this file once to register the command: node deploy-commands.js.

Responding to Commands

Now, update your index.js to handle the interaction when a user executes the command:

client.on('interactionCreate', async interaction => {
    if (!interaction.isChatInputCommand()) return;

    if (interaction.commandName === 'ping') {
        await interaction.reply('Pong!');
    }
});

Step 6: Running the Bot

Update your package.json to include a start script:

"scripts": {
  "start": "node index.js"
}

Now, launch your bot:

npm start

Step 7: Adding Advanced Functionality

A bot is only as useful as the features it provides. Here are three common patterns used in professional Discord development:

1. Embeds for Rich UI

Discord allows "Embeds"—formatted blocks of text that support images, fields, and colors. Use the EmbedBuilder class to create clean responses:

const { EmbedBuilder } = require('discord.js');

const exampleEmbed = new EmbedBuilder()
    .setColor(0x0099FF)
    .setTitle('System Status')
    .setDescription('The bot is running smoothly.');

interaction.reply({ embeds: [exampleEmbed] });

2. Error Handling

Discord API requests are asynchronous and prone to network hiccups. Always wrap your interactions in try-catch blocks:

try {
    await interaction.reply('Pong!');
} catch (error) {
    console.error('Failed to respond to interaction:', error);
}

3. Folder-Based Command Handling

As your bot grows, putting all logic into index.js becomes unmanageable. The standard architectural pattern is to create a commands/ directory:

  1. Create an index.js file in commands/ that exports an object map of command files.
  2. Use fs (Node's File System module) to dynamically load these files during client.once('ready').
  3. This creates a clean "Plugin" architecture where each command lives in its own standalone file.

Best Practices for Tier-1 Developers

Security

  • Environment Variables: Never commit .env to Git. Add it to your .gitignore immediately.
  • Token Regeneration: If you accidentally push your token to a public repository, reset it in the Developer Portal immediately. Discord’s automation often detects leaked tokens and invalidates them automatically.

Performance

  • Rate Limiting: Discord imposes strict rate limits. discord.js handles most of this internally, but be cautious when sending thousands of messages or API requests in a short loop.
  • Caching: By default, discord.js caches objects in memory. For massive bots (1,000+ servers), you should configure the Options property in the Client constructor to limit the cache size and prevent memory leaks.

Deployment

For local development, running node index.js is fine. However, for a production bot:

  • Process Managers: Use pm2 to ensure the bot restarts automatically if it crashes.
  • Containerization: Create a Dockerfile. Discord bots are stateless, making them perfect candidates for deployment on platforms like Fly.io, Railway, or AWS ECS.

Troubleshooting

  • Bot is online but not responding: Check if you have the correct "Intents" enabled in both your code and the Discord Developer Portal.
  • "Missing Access": Ensure your bot is actually in the server. You can generate an invite link in the Developer Portal under OAuth2 > URL Generator. Use the bot and applications.commands scopes.
  • 401 Unauthorized: This almost always means an invalid or expired token in your .env file.

Conclusion

You now have a functional, interaction-ready Discord bot. The ecosystem of discord.js is expansive; once you master the basics of handling interactions and gateway events, you can start building complex integrations, such as database connectivity (MongoDB/PostgreSQL), AI-powered chatbots using OpenAI’s API, or automated moderation systems.

The next step is to explore the official discord.js documentation, which remains the most reliable source for updates and advanced API interactions. Happy coding.

Frequently Asked Questions

How do I create a Discord bot with Node.js?

Install discord.js via npm, create a bot application on the Discord Developer Portal, generate a bot token, and write a script that connects using Client.login(token).

What is discord.js?

discord.js is a powerful Node.js library for interacting with the Discord API. It supports bots, webhooks, voice channels, and slash commands.

Do I need a VPS to run a Discord bot?

No. You can run it locally for testing. For 24/7 uptime, deploy to a VPS (like DigitalOcean or Railway) or use a free tier service like Render.