Skip to main content
When building Discord apps, your app can receive common events from the client as webhooks when users interact with your app through interactions like application commands or message components. Discord will send these events to a pre-configured HTTPS endpoint (called an Interactions Endpoint URL in an app’s configuration) as a JSON payload with details about the event. This tutorial walks through building a Discord app powered by r/aww using JavaScript. All of the code for this app can be found on GitHub.

Features and technologies used


Creating an app on Discord

To start, we’ll create the app through the Discord Developer Dashboard: IDs found in app settings
  • Now click on the Bot tab on the left sidebar.
  • Grab the token for your bot, and store it somewhere safe (I like to put these tokens in a password manager like 1password or lastpass).
For security reasons, you can only view your bot token once. If you misplace your token, you’ll have to generate a new one.

Adding bot permissions

Now we’ll configure the bot with permissions required to create and use slash commands, as well as send messages in channels.
  • Click on the OAuth2 tab, and choose the URL Generator. Click the bot and applications.commands scopes.
  • Check the boxes next to Send Messages and Use Slash Commands, then copy the Generated URL.
Configuring bot permissions in app settings
  • Paste the URL into the browser and follow the OAuth flow, selecting the server where you’d like to develop and test your bot.

Creating your Cloudflare Worker

Cloudflare Workers are a convenient way to host Discord apps due to the free tier, simple development model, and automatically managed environment (no VMs!).
When using Cloudflare Workers, your app won’t be able to access non-ephemeral CDN media. For example, trying to fetch an image like https://cdn.discordapp.com/attachments/1234/56789/my_image.png would result in a 403 error. Cloudflare Workers are still able to access ephemeral CDN media.

Storing secrets

The production service needs access to some of the information we saved earlier. To set those variables, run:
You’ll also need the Guild ID for the server where your app is installed. This can be found in the URL when you visit any channel in that server.
For example, if my URL was https://discord.com/channels/123456/789101112, the Guild ID is the first number—in this case 123456.
Once you know your Guild ID, set that variable as well:

Running locally

This depends on the beta version of the wrangler package, which better supports ESM on Cloudflare Workers.
Let’s start by cloning the repository and installing dependencies. This requires at least v16 of Node.js:

Project structure

A brief look at the cloned app’s project structure:

Registering commands

Before testing our app, we need to register our desired slash commands. For this app, we’ll have a /awwww command, and a /invite command. The name and description for these are kept separate in commands.js:
The code to register commands lives in register.js. Commands can be registered globally, making them available for all servers with the app installed, or they can be registered on a single server. In this example - we’ll just focus on global commands:

Running the server

This command needs to be run locally, once before getting started:
We’re finally ready to run this code locally! Let’s start by running our local development server:

Setting up ngrok

When a user types a slash command, Discord will send an HTTP request to a public endpoint. During local development this can be a little challenging, so we’re going to use a tool called ngrok to create an HTTP tunnel.
ngrok forwarding address This is going to bounce requests off of an external endpoint, and forward them to your machine. Copy the HTTPS link provided by the tool. It should look something like https://8098-24-22-245-250.ngrok.io. Now head back to the Discord Developer Dashboard, and update the Interactions Endpoint URL for your app: Interactions Endpoint URL This is the process we’ll use for local testing and development. When you’ve published your app to Cloudflare, you will want to update this field to use your Cloudflare Worker URL.

Deployment

This repository is set up to automatically deploy to Cloudflare Workers when new changes land on the main branch. To deploy manually, run npm run publish, which uses the wrangler publish command under the hood. Publishing via a GitHub Action requires obtaining an API Token and your Account ID from Cloudflare. These are stored as secrets in the GitHub repository, making them available to GitHub Actions. The following configuration in .github/workflows/ci.yaml demonstrates how to tie it all together:

Code deep dive

Most of the interesting code in this app lives in src/server.js. Cloudflare Workers require exposing a fetch function, which is called as the entry point for each request. This code will largely do two things for us: validate the request is valid and actually came from Discord, and hand the request over to a router to help give us a little more control over execution.
All of the API calls from Discord in this example will be POSTed to /. From here, we will use the discord-interactions npm module to help us interpret the event, and to send results.

Next steps

In case you need to reference any of the code, you can find the repo on GitHub
With your app built and deployed, you can start customizing it to be your own:
  • Use message components in your app to add more interactivity (like buttons and select menus).
  • Take a look at different public APIs on GitHub.
  • Join the Discord Developers server to ask questions about the API, attend events hosted by the Discord API team, and interact with other developers.