DEV Community

Cover image for Getting-Started
Ashley Tonkin
Ashley Tonkin

Posted on • Originally published at itsash.au

Getting-Started

Part 1: From Zero to "It's Alive!"

Part 1 of a beginner-friendly series on building a real Discord bot with JavaScript, discord.js, and Prisma.

So you want to build a Discord bot. Maybe you've seen bots that play music, run polls, hand out roles, or track who's the most active in a server, and thought: how do they even do that? By the end of this series, you'll have built one yourself — and understood every piece along the way.

This first part is all about getting started. We're going to take you from "I've never written a line of code" to a bot that logs into Discord, shows up online, and responds to a command you type. No prior coding experience required. I'll explain not just what to click and type, but why — because once you understand the why, you can build anything, not just copy-paste your way through one tutorial.

Let's go.


What we're building (the whole series)

Before we dive in, here's the map so you know where we're headed:

  • Part 1 (this one): Understand what a bot actually is, set one up in Discord, and get it responding to a slash command.
  • Part 2: Give your bot more commands and learn how to organize your code so it doesn't turn into spaghetti.
  • Part 3: Give your bot a memory using a database. This is where Prisma comes in — a tool that lets your bot save and recall information (like user points, settings, or warnings) even after it restarts.
  • Part 4 and beyond: Build a real feature end to end — think a leveling system or a simple economy — using everything we've learned.

If the word "database" or "Prisma" sounds intimidating right now, don't worry. We won't touch it until Part 3, and by then it'll make complete sense. For now, just keep this in the back of your mind: a bot on its own has no memory. Every time it restarts, it forgets everything. A database is how we fix that — and Prisma is the friendly translator between your JavaScript code and that database.


First, what is a Discord bot?

Let's clear up the biggest source of confusion right away, because it trips up almost everyone at the start.

A Discord bot is not something that lives "inside" Discord. It's a program — code — that runs on a computer (your laptop now, a server later) and logs into Discord over the internet, the same way you log in with the Discord app. Discord just treats it as a special kind of user.

Here's the mental model that makes everything click:

Your bot is a program that connects to Discord, listens for things happening (someone typed a command, someone joined the server), and reacts by doing something (sending a message, giving a role).

That's genuinely the whole idea. Listen for events, react to them. Everything else is detail.

So building a bot involves two separate worlds, and you'll bounce between them:

  1. The Discord side — where you register your bot, give it an identity, and get permission for it to join servers. You do this in a web dashboard.
  2. The code side — where you write the actual instructions for what your bot does. This is the JavaScript.

We'll set up the Discord side first (so our bot exists), then write the code (so it does something).


What you'll need

Three things, and they're all free.

1. A Discord account and a test server

You almost certainly have an account already. You'll also want a server you own to test in — never test on a real community server, because your bot will misbehave while you learn, and that's totally normal.

Making a test server takes ten seconds: in the Discord app, click the + button on the far-left server bar, choose Create My Own, and name it whatever you like ("Bot Lab" works). Done. This is your sandbox.

2. Node.js

Here's the first "why." Your bot is written in JavaScript — the same language that runs websites in your browser. But your bot doesn't run in a browser; it runs on a computer as a standalone program. Node.js is the thing that lets JavaScript run outside the browser. Think of it as the engine that takes your .js files and actually executes them.

Go to nodejs.org and download the LTS version (LTS means "Long-Term Support" — the stable, reliable one that most people use). Anything version 22.12.0 or newer will work with the tools in this series, and the current LTS is comfortably past that.

To check it worked, you'll use a terminal. Don't be scared of it — a terminal is just a text box where you type commands to your computer instead of clicking buttons. On Windows, open PowerShell (search for it in the Start menu); on Mac, open Terminal (search with Cmd+Space). Type this and press Enter:

node --version
Enter fullscreen mode Exit fullscreen mode

If you see something like v22.12.0 (or higher), you're good. That number is Node telling you it's installed and ready.

3. A code editor

You could write code in Notepad, but a proper code editor makes life dramatically easier — it color-codes your code, catches typos, and has a built-in terminal. The standard choice (and what I'd recommend) is Visual Studio Code, usually just called "VS Code." It's free. Download it, install it, open it.

That's the whole toolkit. Now let's make our bot exist.


Step 1 — Create your application in the Discord Developer Portal

Everything on the Discord side happens in one place: the Developer Portal. Head to discord.com/developers/applications and sign in with your normal Discord account.

Click New Application in the top right. Give it a name (this becomes your bot's name, but you can change it later), check the box to agree to Discord's terms, and click Create.

A quick note on vocabulary, because Discord uses two words that sound the same but aren't:

  • An application is the overall project — a container that holds your bot's identity, settings, and permissions.
  • A bot is the actual user account that joins servers and sends messages.

You just made the application. In the next step, you'll find the bot living inside it. One application, one bot — that's the relationship.


Step 2 — Find your bot and get its token

In the left-hand menu of your new application, click Bot.

This page is your bot's identity. You can give it a profile picture and username here. But the important part is the token.

What a token is (and why you must guard it)

Scroll to the Token section and click Reset Token (you may need to confirm). Discord will show you a long string of random-looking characters. Copy it now and keep it somewhere safe for a minute — Discord only shows it once, and you'll need it shortly.

Now, the most important sentence in this entire article:

Your token is your bot's password. Anyone who has it can fully control your bot.

It's not like a password — it genuinely is the credential that proves "I am this bot" to Discord. If someone gets your token, they can make your bot do anything: spam servers, delete messages, get you banned. There is no "I forgot my password" recovery that protects you here.

So three rules, starting right now:

  1. Never paste your token into a chat, screenshot, forum post, or a question to a stranger.
  2. Never put your token directly in your code where it could get shared.
  3. If a token ever leaks, click Reset Token immediately — that instantly invalidates the old one.

We'll handle rule #2 properly in a few minutes using something called a .env file. Keep that copied token handy.

While you're here: intents

Scroll down a little to Privileged Gateway Intents. You'll see toggles for Presence, Server Members, and Message Content, all switched off.

Here's the why. When your bot connects, it would be wasteful (and a privacy problem) for Discord to fire off every single event happening in every server to every bot. So Discord uses intents — think of them as subscriptions. Your bot says "I'm interested in these kinds of events," and Discord only sends those. It keeps bots efficient and respects users' data.

Three intents are privileged, meaning they're sensitive enough to be off by default:

  • Presence — seeing when users go online, idle, or offline.
  • Server Members — getting notified when people join or leave, and reading the full member list.
  • Message Content — actually reading the text of normal messages.

That last one matters a lot in 2026: Discord now strictly enforces it. If your bot tries to read message text without enabling Message Content (both here in the portal and in your code), Discord just hands back an empty string. It's a deliberate privacy guardrail.

Good news for us: for this entire first bot, you don't need to enable any of these. We're going to use slash commands, and as you'll see, slash commands deliver their data straight to your bot — so we never need to read raw message text. That's the modern, privacy-friendly way to build, and it means you can leave all three toggles off. Just know what they are for when a future bot needs them.


Step 3 — Invite your bot to your server

Right now your bot exists but isn't in any server. Let's fix that.

In the left menu, go to OAuth2, then find the OAuth2 URL Generator. This tool builds a special invite link for your bot. (OAuth2 is just the standard system websites use to grant apps permission to act on your behalf — it's the same machinery behind "Log in with Google.")

Under Scopes, check two boxes:

  • bot — this says "I want to add this as a bot to a server."
  • applications.commands — this says "allow this bot to register slash commands." Without it, the / commands we're about to build won't appear. Easy to forget, so don't.

When you check bot, a Bot Permissions panel appears below. For now, tick Send Messages. (Permissions control what your bot is allowed to do once it's in a server — separate from intents, which control what it gets to see.)

Now scroll to the bottom, copy the generated URL, and paste it into your browser. Discord will ask which server to add the bot to — pick your test server, click Authorize, and complete the little human check.

Pop back into Discord and look at your test server's member list. Your bot is there! It'll show as offline for now — that's expected. A bot only appears "online" when its code is actually running. Which is exactly what we're about to make happen.


Step 4 — Set up your project

Time to switch to the code side. Open VS Code, then open its built-in terminal with Terminal → New Terminal from the top menu. This terminal is where we'll run commands; the file area is where we'll write code.

First, make a folder for your bot and move into it. In the terminal:

mkdir my-discord-bot
cd my-discord-bot
Enter fullscreen mode Exit fullscreen mode

mkdir makes a directory (a folder), and cd changes directory into it. Everything we do now lives in this folder.

Create the project

Run this:

npm init -y
Enter fullscreen mode Exit fullscreen mode

npm is the Node Package Manager — it comes free with Node.js and its job is to install and manage code libraries other people have written (so you don't reinvent the wheel). This command creates a file called package.json, which is basically your project's ID card: it records your project's name, version, and — importantly — the list of libraries it depends on. The -y just says "yes to all the default settings."

Install the libraries we need

npm install discord.js dotenv
Enter fullscreen mode Exit fullscreen mode

This downloads two libraries into a node_modules folder and records them in your package.json:

  • discord.js — the star of the show. It's a library that wraps Discord's complicated raw API in friendly JavaScript, so instead of manually crafting web requests, you write things like client.login(). We're using version 14, the current line.
  • dotenv — a tiny helper that loads secrets (like your token) from a separate file into your program. This is how we'll keep that token out of our code.

Protect your secrets

Create two new files in your project folder (in VS Code, click the "new file" icon in the file panel).

First, a file named exactly .env (yes, it starts with a dot). This is where your secrets live. Paste in the following, replacing the placeholder with the token you copied earlier:

DISCORD_TOKEN=paste-your-bot-token-here
Enter fullscreen mode Exit fullscreen mode

Second — and this is the part people forget — create a file named .gitignore with this inside:

node_modules/
.env
Enter fullscreen mode Exit fullscreen mode

Here's the why, and it ties straight back to our token rule. Later, you'll likely use Git to back up your code or share it (for example on GitHub). A .gitignore file tells Git "never include these." We're excluding .env so your secret token never gets uploaded anywhere public, and node_modules because it's huge and anyone can re-download it with npm install. Setting this up now, before you ever touch Git, means you can never accidentally leak your token. That's not paranoia — leaked bot tokens are one of the most common beginner mistakes, and bots get scanned for constantly.


Step 5 — Write the bot and bring it online

Create one more file, named index.js. This is your bot's brain. Type (don't just paste — typing it helps it sink in) the following:

// Load the secrets from our .env file into the program
require('dotenv').config();

// Pull the tools we need out of the discord.js library
const { Client, Events, GatewayIntentBits } = require('discord.js');

// Create our bot ("client") and tell Discord which events we care about
const client = new Client({
  intents: [GatewayIntentBits.Guilds],
});

// This runs ONCE, the moment the bot successfully logs in
client.once(Events.ClientReady, (readyClient) => {
  console.log(`✅ Logged in as ${readyClient.user.tag}`);
});

// Finally: log in using the token from our .env file
client.login(process.env.DISCORD_TOKEN);
Enter fullscreen mode Exit fullscreen mode

Let's walk through why each piece is there, top to bottom:

  • require('dotenv').config() runs first so that our secret token is loaded and ready before anything else needs it. require(...) is simply how Node.js pulls in a library.
  • const { Client, Events, GatewayIntentBits } = require('discord.js') grabs three specific tools out of discord.js: Client (your bot), Events (a tidy list of event names), and GatewayIntentBits (those intent subscriptions from Step 2).
  • new Client({ intents: [GatewayIntentBits.Guilds] }) creates your bot and subscribes it to basic server-related events. Notice we only ask for Guilds ("guild" is Discord's internal word for "server") — no privileged intents needed, exactly as promised.
  • client.once(Events.ClientReady, ...) registers a reaction to the "I'm connected!" event. once means it only fires a single time. (Small but useful detail: discord.js is in the middle of renaming this event from ready to clientReady. By using the Events.ClientReady enum instead of typing the name as text, you're shielded from that change — your code keeps working across versions. A nice example of why using provided constants beats hardcoding strings.)
  • client.login(process.env.DISCORD_TOKEN) is the moment of truth — it connects to Discord using your token. process.env.DISCORD_TOKEN is how we read the value dotenv loaded from .env.

Run it

Back in the terminal:

node index.js
Enter fullscreen mode Exit fullscreen mode

If everything's right, you'll see ✅ Logged in as YourBotName#0000 in the terminal — and if you flip over to Discord, your bot is now showing as online. That little green dot is your code, running, connected to Discord. Take a second to enjoy it. You built that.

To stop the bot, click the terminal and press Ctrl + C. (And yes — when you stop it, the bot goes offline. It's only alive while the program runs. Keeping it online 24/7 means keeping the program running somewhere, which is a later-part topic.)


Step 6 — Your first slash command

An online bot that does nothing is a bit anticlimactic. Let's make it respond to /ping.

Why slash commands? A few years ago, bots worked by reading every message and checking if it started with something like !ping. That required reading all message text (that privileged Message Content intent), and it was clunky. Slash commands are Discord's modern, built-in system: users type /, Discord shows them your bot's available commands, and when they pick one, Discord sends your bot a neat package of exactly what was requested. Cleaner for users, and no need to snoop on message content. Everybody wins.

There are two stages: first you register the command with Discord (tell it the command exists), then you handle it in your bot (decide what to do when someone uses it).

Register the command

Slash commands have to be registered with Discord before they show up. Create a new file called deploy-commands.js:

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

// Define our commands
const commands = [
  new SlashCommandBuilder()
    .setName('ping')
    .setDescription('Replies with Pong!'),
].map((command) => command.toJSON());

// Set up a connection to Discord's API using our token
const rest = new REST().setToken(process.env.DISCORD_TOKEN);

// Send the commands to Discord
(async () => {
  try {
    console.log('Registering slash commands...');
    await rest.put(
      Routes.applicationGuildCommands(process.env.CLIENT_ID, process.env.GUILD_ID),
      { body: commands },
    );
    console.log('✅ Slash commands registered!');
  } catch (error) {
    console.error(error);
  }
})();
Enter fullscreen mode Exit fullscreen mode

This needs two new pieces of info — your application's ID and your server's ID — so add them to your .env file:

DISCORD_TOKEN=paste-your-bot-token-here
CLIENT_ID=paste-your-application-id-here
GUILD_ID=paste-your-server-id-here
Enter fullscreen mode Exit fullscreen mode

Where to find them:

  • CLIENT_ID — in the Developer Portal, open your app, go to General Information, and copy the Application ID.
  • GUILD_ID — in the Discord app, you first need Developer Mode on: User Settings → Advanced → Developer Mode. Then right-click your test server's icon and choose Copy Server ID.

Now register the command by running the file once:

node deploy-commands.js
Enter fullscreen mode Exit fullscreen mode

You should see ✅ Slash commands registered!.

One worthwhile "why" here: we registered to a specific guild (applicationGuildCommands). Guild commands appear instantly, which is perfect while testing. You can register commands globally for all servers, but those can take up to an hour to roll out — so we'll stick with guild commands for now. You only need to re-run this script when you add or change commands, not every time you start the bot.

Handle the command

Now teach the running bot what to do when /ping is used. Back in index.js, add this new block just below your ClientReady block (above the client.login line):

// This runs EVERY time someone uses a slash command
client.on(Events.InteractionCreate, async (interaction) => {
  // Ignore anything that isn't a slash command
  if (!interaction.isChatInputCommand()) return;

  if (interaction.commandName === 'ping') {
    await interaction.reply('Pong! 🏓');
  }
});
Enter fullscreen mode Exit fullscreen mode

Why it's written this way:

  • client.on(Events.InteractionCreate, ...) uses on (not once) because we want to react every time, not just the first. An "interaction" is Discord's word for a user engaging with your bot — a slash command is one kind.
  • if (!interaction.isChatInputCommand()) return is a safety guard. Interactions include buttons, menus, and more; this line says "if it's not a typed slash command, stop here and ignore it."
  • interaction.commandName === 'ping' checks which command was used, so you can branch to different behavior per command later.
  • await interaction.reply('Pong! 🏓') sends the response. The await matters: talking to Discord happens over the internet and takes a tiny moment, so await says "wait for that to finish before moving on." (That's also why the function is marked async — the two always travel together.)

See it work

Start the bot again:

node index.js
Enter fullscreen mode Exit fullscreen mode

Hop into your test server, type / in any channel, pick ping from the menu Discord shows you, and hit Enter. Your bot replies Pong! 🏓.

That's a complete, working, modern Discord bot. 🎉


When things go wrong (they will — that's normal)

A few of the most common snags, and what they usually mean:

  • Bot stays offline / "An invalid token was provided." Your DISCORD_TOKEN in .env is wrong or has extra spaces/quotes. Re-copy it from the portal (Reset Token if needed). Tokens go in .env with no quotes around them.
  • /ping doesn't appear in Discord. Either you didn't run node deploy-commands.js, or your CLIENT_ID / GUILD_ID are off, or you forgot the applications.commands scope when inviting the bot. Re-invite with that scope checked if so.
  • Cannot find module 'discord.js'. You're not in the right folder, or you skipped npm install. Make sure your terminal is inside my-discord-bot and run npm install discord.js dotenv again.
  • Nothing happens when you run a command. Make sure the bot is actually running (node index.js in a terminal you haven't closed) at the moment you use the command.

Reading error messages is a real skill, not a sign you're bad at this. Every developer Googles their errors constantly — that's the job, not a failure.


What you just learned

Step back and notice how much you now understand:

  • A bot is just a program that connects to Discord, listens for events, and reacts — not magic, not something hidden inside Discord.
  • The Developer Portal is where your bot gets its identity, and its token is a password you protect with a .env file and .gitignore.
  • Intents are event subscriptions, and slash commands let you build powerful bots without ever reading raw messages.
  • You wired up npm, discord.js, and dotenv, and got a real command responding in your server.

That foundation carries the whole rest of the series.

Coming up in Part 2

Right now everything lives in one file, and our bot knows exactly one command. In Part 2, we'll add more commands and reorganize the project so each command lives in its own tidy file — the structure real bots use, so things stay manageable as they grow.

And then in Part 3, we tackle the big one: memory. Notice that if you stop and restart your bot, it remembers nothing — there's nowhere for it to store information. We'll introduce a database to fix that, and use Prisma to talk to it in clean, readable JavaScript instead of raw database commands. That's when your bot stops being a toy and starts being able to remember things about your server.

See you in Part 2. Now go change your bot's reply to something other than "Pong!" — seriously, go break it and fix it. That's how this sticks. 🚀


Found this helpful? Part 2 builds directly on the project you just made, so keep this folder around.

Top comments (0)