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.
Ein PlayNHost-Bot-Tarif vom Typ Node.js Bot. Jeder Tarif enthält eine Datenbank.
1. Die Datenbank erstellen
Öffne deinen Bot-Server im Panel und klicke auf Datenbanken.
Klicke auf Neue Datenbank. Eine Bezeichnung ist optional; unter Wird erstellt als siehst du den vollständigen Namen. Klicke auf Datenbank erstellen.
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
Öffne die Seite Bot, den Bereich Umgebung, und klicke auf Eine .env-Datei einfügen.
Füge diese fünf Zeilen mit den Werten aus der Karte Verbindung ein:
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
Lade index.js und package.json unter Dateien hoch. Lade node_modules nicht hoch.
Klicke auf Starten und öffne Konsole. Beim Start werden die Pakete installiert, danach erscheinen [PlayNHost] Starting your bot, Database ready und Logged in as ....
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.
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?
Wir verwenden Cookies, damit die Website funktioniert, um ihre Nutzung zu verstehen und unser Marketing zu messen. Du kannst alles akzeptieren, nur essenzielle Cookies akzeptieren oder pro Kategorie wählen.Wir verwenden Cookies, damit die Website funktioniert, um ihre Nutzung zu verstehen und unser Marketing zu messen. Sie sind hier standardmäßig aktiv. Du kannst alles abschalten, was du nicht möchtest.Datenschutzerklärung lesen