NEU 📢 Begrenztes Startangebot: bis zu 70 % Rabatt auf deinen ersten Monat, für jedes Spiel!Tarife ansehen

MySQL-Datenbank mit einem discord.js-Bot nutzen (mysql2)

Discord-Bot-HostingUpdated Nov 6, 20268 min read

Kurzantwort. Füge mysql2 zu deinem discord.js-Bot hinzu, erstelle beim Start des Bots einen Verbindungspool aus Umgebungsvariablen und führe jede Abfrage darüber mit ?-Platzhaltern aus. Bei PlayNHost enthält jeder Bot-Tarif eine MySQL-Datenbank: Lege sie auf der Seite Datenbanken an, füge ihre Zugangsdaten im Bereich Umgebung ein, und der Code unten funktioniert so, wie er ist. Die Datenbank nimmt Verbindungen von deinen PlayNHost-Servern und von phpMyAdmin an, nicht von deinem eigenen Rechner. Wir haben den Code am 6. November 2026 mit discord.js 14.28.1, mysql2 3.24.6 und Node.js 22 getestet, gegen MariaDB 10.11 mit einem Datenbankkonto, das wie unseres eingerichtet ist (20 Verbindungen, 120 Sekunden Abfragelimit): Start, Tabelle und den /warn-Handler, aber keinen echten Discord-Login.

Was du brauchst

1. Die Datenbank erstellen

  1. Öffne deinen Bot-Server im Panel und klicke auf Datenbanken.
  2. Klicke auf Neue Datenbank. Eine Bezeichnung ist optional; unter Wird erstellt als siehst du den vollständigen Namen. Klicke auf Datenbank erstellen.
  3. Die Seite der neuen Datenbank öffnet sich (später klickst du sie unter Deine Datenbanken an). Ihre Karte Verbindung zeigt Host, Port, Datenbank, Benutzername und Passwort. Unter Fertige Config bietet Config für einen Ausschnitt Node.js (mysql2) mit denselben, bereits eingetragenen Werten.

2. Die Zugangsdaten in „Umgebung“ eintragen

  1. Öffne die Seite Bot, den Bereich Umgebung, und klicke auf Eine .env-Datei einfügen.
  2. Füge diese fünf Zeilen mit den Werten aus der Karte Verbindung ein:
    DB_HOST=your-host
    DB_PORT=3306
    DB_NAME=your-database
    DB_USER=your-username
    DB_PASSWORD=your-password
  3. DB_PASSWORD bekommt ein Secret-Badge: Es wird nur schreibend gespeichert und nie wieder angezeigt. Klicke auf 5 Variablen importieren.

Nimm den echten Port aus der Karte Verbindung. Wenn das Passwort nicht im Code steht, landet es weder in einer Datei noch in einem Git-Repository. Mehr zu diesem Bereich: Umgebungsvariablen und Secrets für deinen Discord-Bot setzen.

3. Der Code

package.json

{
  "name": "my-discord-bot",
  "private": true,
  "main": "index.js",
  "dependencies": {
    "discord.js": "^14",
    "mysql2": "^3"
  }
}

index.js

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

// One pool for the whole bot. The values come from the Environment pane.
const pool = mysql.createPool({
  host: process.env.DB_HOST,
  port: Number(process.env.DB_PORT),
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  database: process.env.DB_NAME,
  connectionLimit: 5,
});

const commands = [
  new SlashCommandBuilder()
    .setName('warn')
    .setDescription('Warn a member and count their warnings')
    .setContexts(InteractionContextType.Guild)
    .addUserOption((o) => o.setName('user').setDescription('Who to warn').setRequired(true))
    .addStringOption((o) => o.setName('reason').setDescription('Why').setRequired(true).setMaxLength(200))
    .toJSON(),
];

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

client.once(Events.ClientReady, async () => {
  await new REST().setToken(process.env.DISCORD_TOKEN).put(Routes.applicationCommands(client.user.id), { body: commands });
  console.log(`Logged in as ${client.user.tag}`);
});

client.on(Events.InteractionCreate, async (interaction) => {
  if (!interaction.isChatInputCommand() || interaction.commandName !== 'warn' || !interaction.guildId) return;
  const user = interaction.options.getUser('user', true);
  const reason = interaction.options.getString('reason', true);
  try {
    await pool.execute('INSERT INTO warnings (guild_id, user_id, reason) VALUES (?, ?, ?)', [interaction.guildId, user.id, reason]);
    const [rows] = await pool.execute('SELECT COUNT(*) AS total FROM warnings WHERE guild_id = ? AND user_id = ?', [interaction.guildId, user.id]);
    await interaction.reply(`${user} has ${rows[0].total} warning(s).`);
  } catch (err) {
    console.error(err);
    if (!interaction.replied) await interaction.reply({ content: 'Could not save the warning. Try again later.', flags: MessageFlags.Ephemeral }).catch(console.error);
  }
});

async function main() {
  await pool.query(`CREATE TABLE IF NOT EXISTS warnings (
    id INT AUTO_INCREMENT PRIMARY KEY,
    guild_id VARCHAR(20) NOT NULL,
    user_id VARCHAR(20) NOT NULL,
    reason VARCHAR(200) NOT NULL,
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    KEY guild_user (guild_id, user_id)
  )`);
  console.log('Database ready');
  await client.login(process.env.DISCORD_TOKEN);
}

main().catch((err) => {
  console.error(err);
  process.exit(1);
});

So funktioniert es: Beim Start legt der Bot die Tabelle warnings an, falls sie fehlt, gibt Database ready aus und meldet sich dann an. /warn speichert eine Verwarnung und antwortet mit der Gesamtzahl des Mitglieds. discord.js liefert Discord-IDs als Strings, weil sie für eine JavaScript-Zahl zu groß sind, deshalb werden sie als Text gespeichert. Die ?-Platzhalter schicken den Text des Mitglieds getrennt vom SQL an die Datenbank, sodass ein Grund wie '); DROP TABLE warnings; -- als einfacher Text gespeichert wird.

Zwei Zeilen halten den Bot am Leben. setContexts(InteractionContextType.Guild) und die Prüfung !interaction.guildId verhindern /warn in einer Direktnachricht, wo es keinen Server zum Speichern gibt. Das try/catch antwortet mit einer kurzen privaten Nachricht, wenn eine Abfrage fehlschlägt. Ohne es gibt discord.js den Fehler an sein error-Event weiter, und wenn dort niemand zuhört, beendet Node.js den Bot. In unserem Test haben wir die Tabelle gelöscht und /warn ausgeführt: Der Bot hat ER_NO_SUCH_TABLE geloggt, mit Could not save the warning. Try again later. geantwortet und lief weiter.

4. Starten

  1. Lade index.js und package.json unter Dateien hoch. Lade node_modules nicht hoch.
  2. Klicke auf Starten und öffne Konsole. Beim Start werden die Pakete installiert, danach erscheinen [PlayNHost] Starting your bot, Database ready und Logged in as ....
  3. Tippe in Discord /warn ein. Um die Zeilen anzusehen, öffne die Seite der Datenbank und klicke auf In phpMyAdmin öffnen.

Limits, die für deinen Pool wichtig sind

  • 20 gleichzeitige Verbindungen pro Datenbankbenutzer. Jeder Prozess mit demselben Login zählt mit: dein Bot, eine zweite Kopie davon, ein Dashboard. Verbindung 21 wird mit ER_USER_LIMIT_REACHED abgelehnt. In unserem Test scheiterte ein Pool mit connectionLimit: 30 mit diesem Fehler, sobald 30 Abfragen gleichzeitig liefen. Ein Pool mit 5 öffnet nie mehr als 5: Weitere Abfragen warten auf eine freie Verbindung.
  • Eine Abfrage, die länger als 120 Sekunden läuft, wird abgebrochen. MariaDB beendet sie mit dem Fehler 1969, Query execution was interrupted (max_statement_time exceeded). In unserem Test kam das nach 120.03 Sekunden. Die Abfragen eines Bots dauern normalerweise Millisekunden, das trifft also nur eine außer Kontrolle geratene Abfrage.
  • 500 MB pro Datenbank. Die Seite Datenbanken zeigt, wie viel belegt ist. Bei 500 MB werden Schreibzugriffe gestoppt, bis du Zeilen löschst.
  • Server-Backups enthalten die Datenbank nicht. Exportiere sie ab und zu aus phpMyAdmin.

Fehler und was sie bedeuten

Das sind die genauen Meldungen, die mysql2 in unserem Test lieferte:

  • ER_ACCESS_DENIED_ERROR ... Access denied for user: falscher Wert bei DB_USER oder DB_PASSWORD. Nach Passwort zurücksetzen auf der Karte Verbindung funktioniert das alte Passwort sofort nicht mehr: Ersetze DB_PASSWORD und starte den Bot neu.
  • ECONNREFUSED: Unter diesem Host und Port hat niemand die Verbindung angenommen. Prüfe DB_HOST und DB_PORT. Auch ein fehlendes DB_HOST endet hier, weil mysql2 sich dann mit localhost verbindet, wo keine Datenbank läuft: Die Variable fehlt in Umgebung, oder der Bot wurde nach dem Hinzufügen nicht neu gestartet.
  • ENOTFOUND: DB_HOST ist falsch geschrieben, der Name existiert also nicht.
  • ETIMEDOUT: Innerhalb von 10 Sekunden hat nichts geantwortet, das ist die Standardwartezeit von mysql2. Egal welche Meldung: Eine Verbindung von deinem eigenen Rechner scheitert, denn die Datenbank nimmt nur Verbindungen von PlayNHost-Servern und phpMyAdmin an.
  • ER_USER_LIMIT_REACHED ... has exceeded the 'max_user_connections' resource (current value: 20): zu viele offene Verbindungen, siehe die Limits oben. Meist wird pro Befehl eine neue Verbindung geöffnet und nie geschlossen, oder es läuft eine zweite Kopie des Bots.

Weitere Bot-Fehler: Häufige Discord-Bot-Fehler und was sie bedeuten.

Wie viel Arbeitsspeicher es dazukommt

Die Installation von discord.js und mysql2 von Grund auf erreichte in unserem Test einen Spitzenwert von 147 MB Arbeitsspeicher (Dateicache nicht mitgezählt), in einem Container mit 512 MB Limit wie bei unserem kleinsten Bot-Tarif. Vom Start, bis Discord unser Test-Token abgelehnt hat, brauchte der Bot höchstens 31 MB; er hatte die Tabelle angelegt, sich aber nie angemeldet. Ein angemeldeter Bot auf vielen Servern braucht mehr: Wie viel RAM braucht ein Discord-Bot?

Dasselbe in Python oder mit Prisma

Du brauchst einen Bot-Server mit inklusiver Datenbank? Zu den Bot-Tarifen.

Läuft dein Bot schon woanders? Zieh ihn zu uns um

UNSERE NEUESTEN BLOGS