Building a Discord Bot Web Dashboard with Express and OAuth2

Published on

How to create a web dashboard for your Discord bot. Covers OAuth2 authentication, Express.js setup, server management UI, and API design.

Written by Space-Node Team – Infrastructure Team – 15+ years combined experience in game server hosting, VPS infrastructure, and 24/7 streaming solutions. Read author bio →

A web dashboard gives server admins a visual interface to configure your bot without using Discord commands. It's the feature that separates hobbyist bots from professional ones.

Architecture

Browser → Express.js Web Server → Bot API → Discord.js Bot
              ↓
         Discord OAuth2

Users authenticate with their Discord account. The dashboard shows servers they manage. They configure bot settings through a web UI.

OAuth2 Setup

Create Application

  1. Go to Discord Developer Portal
  2. Select your application
  3. OAuth2 → Add redirect: http://yourdomain.com/callback
  4. Note your Client ID and Client Secret

Express.js OAuth2 Flow

const express = require('express');
const fetch = require('node-fetch');
const session = require('express-session');

const app = express();
app.use(session({ secret: 'your-session-secret', resave: false, saveUninitialized: false }));

// Login redirect
app.get('/login', (req, res) => {
    const params = new URLSearchParams({
        client_id: CLIENT_ID,
        redirect_uri: 'http://yourdomain.com/callback',
        response_type: 'code',
        scope: 'identify guilds'
    });
    res.redirect(`https://discord.com/api/oauth2/authorize?${params}`);
});

// OAuth2 callback
app.get('/callback', async (req, res) => {
    const code = req.query.code;
    
    const tokenResponse = await fetch('https://discord.com/api/oauth2/token', {
        method: 'POST',
        body: new URLSearchParams({
            client_id: CLIENT_ID,
            client_secret: CLIENT_SECRET,
            grant_type: 'authorization_code',
            code: code,
            redirect_uri: 'http://yourdomain.com/callback'
        })
    });
    
    const tokens = await tokenResponse.json();
    
    // Get user info
    const userResponse = await fetch('https://discord.com/api/users/@me', {
        headers: { Authorization: `Bearer ${tokens.access_token}` }
    });
    
    const user = await userResponse.json();
    req.session.user = user;
    req.session.accessToken = tokens.access_token;
    
    res.redirect('/dashboard');
});

Dashboard Pages

Server List

Show servers where the user has MANAGE_SERVER permission:

app.get('/dashboard', async (req, res) => {
    if (!req.session.user) return res.redirect('/login');
    
    const guildsResponse = await fetch('https://discord.com/api/users/@me/guilds', {
        headers: { Authorization: `Bearer ${req.session.accessToken}` }
    });
    
    const guilds = await guildsResponse.json();
    const manageable = guilds.filter(g => (g.permissions & 0x20) === 0x20);
    
    // Filter to guilds where bot is present
    const botGuilds = manageable.filter(g => client.guilds.cache.has(g.id));
    
    res.render('dashboard', { guilds: botGuilds, user: req.session.user });
});

Server Settings

app.get('/dashboard/:guildId', async (req, res) => {
    const guild = client.guilds.cache.get(req.params.guildId);
    if (!guild) return res.status(404).send('Bot not in this server');
    
    const settings = await getGuildSettings(req.params.guildId);
    const channels = guild.channels.cache.filter(c => c.type === 0); // Text channels
    
    res.render('server-settings', { guild, settings, channels });
});

app.post('/dashboard/:guildId/settings', async (req, res) => {
    await updateGuildSettings(req.params.guildId, {
        prefix: req.body.prefix,
        welcomeChannel: req.body.welcomeChannel,
        logChannel: req.body.logChannel
    });
    
    res.redirect(`/dashboard/${req.params.guildId}`);
});

Frontend

Use a simple template engine or React for the frontend. Key pages:

  • Login page
  • Server selection
  • Server settings (prefix, channels, features)
  • Moderation logs viewer
  • Analytics/statistics

API Design

Separate the dashboard API from the bot:

// Bot exposes internal API
const apiApp = express();

apiApp.get('/api/guilds/:id/settings', authenticateInternal, async (req, res) => {
    const settings = await getGuildSettings(req.params.id);
    res.json(settings);
});

apiApp.post('/api/guilds/:id/settings', authenticateInternal, async (req, res) => {
    await updateGuildSettings(req.params.id, req.body);
    res.json({ success: true });
});

Hosting

The dashboard needs:

  • Express.js server (port 80/443)
  • SSL certificate (Let's Encrypt)
  • Domain name

On Space-Node VPS hosting, run both the bot and dashboard on the same server. A 2-core VPS handles both comfortably.

For bot-only hosting on Discord Bot plans, the dashboard would need separate web hosting.

Security Notes

  • Store tokens securely (environment variables, not code)
  • Validate that users have permission to manage the guild they're accessing
  • Rate limit API endpoints
  • Use HTTPS in production
  • Don't expose your bot token through the dashboard
Space-Node Team

About the Author

Space-Node Team – Infrastructure Team – Experts in game server hosting, VPS infrastructure, and 24/7 streaming solutions with 15+ years combined experience.

Since 2023
500+ servers hosted
4.8/5 avg rating

Our team specializes in Minecraft, FiveM, Rust, and 24/7 streaming infrastructure, operating enterprise-grade AMD Ryzen 9 hardware in Netherlands datacenters. We maintain GDPR compliance and ISO 27001-aligned security standards.

View Space-Node's full team bio and credentials →

Launch Your VPS Today

Get started with professional VPS hosting powered by enterprise hardware. Instant deployment and 24/7 support included.

Building a Discord Bot Web Dashboard with Express and OAuth2