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

So nutzt du eine MySQL-Datenbank mit einem grammY-Bot (mysql2)

Telegram-Bot-HostingUpdated Nov 6, 202611 min read

Kurzantwort. Füge mysql2 zu deinem grammY-Bot hinzu, erstelle beim Start des Bots einen Verbindungspool aus Umgebungsvariablen, lege deine Tabellen vor bot.start()an und führe jede Abfrage über den Pool aus. Nutze ? -Platzhalter und speichere Telegram-IDs als BIGINT, nicht als INT. Bei PlayNHost enthält jeder Bot-Tarif eine MySQL-Datenbank: Erstelle sie auf der Seite Datenbanken , füge ihre Zugangsdaten in den Bereich Umgebung ein, und der Code unten funktioniert unverändert. Die Datenbank akzeptiert Verbindungen von deinen PlayNHost-Servern und von phpMyAdmin, nicht von deinem eigenen Computer. Wir haben diesen Code am 6. November 2026 mit grammY 1.46.0, 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), und mit einem Ersatz für den Bot API-Server von Telegram: 8 Befehle hinein, 8 Antworten heraus, die Notizen in der Datenbank gespeichert. Ein echter Telegram-Login war nicht Teil des Tests.

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; Wird erstellt als zeigt den vollständigen Namen. Klicke auf Datenbank erstellen.
  3. Die Seite der neuen Datenbank öffnet sich (später klickst du sie unter Deine Datenbankenan). Ihre Karte Verbindung zeigt Host, Port, Datenbank, Benutzername und Passwort. Unter Fertige Confighat Config für einen Ausschnitt Node.js (mysql2) mit denselben, bereits eingetragenen Werten.

2. Die Zugangsdaten in Umgebung eintragen

  1. Öffne die Seite Bot , 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 Abzeichen Secret : Es wird nur schreibend gespeichert und nie wieder angezeigt. Klicke auf 5 Variablen importieren.

Nutze den echten Port aus der Karte Verbindung . Das Bot-Token gehört nicht zu diesen Zeilen: Das Token, das du auf der Seite Bot im Bereich Telegramgespeichert hast, kommt schon als TELEGRAM_BOT_TOKENin deinem Code an, so heißt es im Code unten (und als BOT_TOKEN, TOKEN, TELEGRAM_TOKEN und TELOXIDE_TOKEN). Hat deine .env -Datei eine Token-Zeile, überspringt der Dialog sie. Mehr zu diesem Bereich: Umgebungsvariablen und Secrets für deinen Discord-Bot setzen.

3. Der Code

Ein kleiner Notiz-Bot: /note buy milk speichert eine Notiz, /notes zeigt deine letzten fünf.

package.json

{
  "name": "my-grammy-bot",
  "private": true,
  "main": "index.js",
  "dependencies": {
    "grammy": "^1.46.0",
    "mysql2": "^3.24.6"
  }
}

index.js

const { Bot } = require("grammy");
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),
  database: process.env.DB_NAME,
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  connectionLimit: 5,
});

const bot = new Bot(process.env.TELEGRAM_BOT_TOKEN);

bot.command("note", async (ctx) => {
  const text = ctx.match.trim();
  if (!text) return ctx.reply("Write your note after the command, for example: /note buy milk");
  if (text.length > 500) return ctx.reply("A note can be up to 500 characters.");
  await pool.execute("INSERT INTO notes (user_id, text) VALUES (?, ?)", [ctx.from.id, text]);
  await ctx.reply("Saved. Send /notes to see your last five.");
});

bot.command("notes", async (ctx) => {
  const [rows] = await pool.execute(
    "SELECT text FROM notes WHERE user_id = ? ORDER BY id DESC LIMIT 5",
    [ctx.from.id],
  );
  if (rows.length === 0) return ctx.reply("You have no notes yet.");
  await ctx.reply(rows.map((row) => `- ${row.text}`).join("\n"));
});

// Without this, an error in one command stops the whole bot.
bot.catch((err) => {
  console.error(`Error while handling update ${err.ctx.update.update_id}:`, err.error);
});

async function main() {
  await pool.query(
    "CREATE TABLE IF NOT EXISTS notes (" +
      "id INT AUTO_INCREMENT PRIMARY KEY, " +
      "user_id BIGINT NOT NULL, " +
      "text VARCHAR(500) NOT NULL, " +
      "created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, " +
      "KEY user_id (user_id))",
  );
  console.log("Database ready");
  // bot.start() only returns when the bot stops, so the ready line goes in onStart.
  await bot.start({ onStart: (me) => console.log(`Logged in as @${me.username}`) });
}

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

Was er tut: main() legt die Tabelle notes an, falls sie fehlt, und gibt Database ready aus, bevor bot.start() bei Telegram Nachrichten abholt. Der Pool wird einmal am Anfang der Datei erstellt und jeder Befehl nutzt ihn: pool.execute() leiht sich eine Verbindung, führt die Abfrage aus und gibt die Verbindung zurück. ctx.match ist der Text nach dem Befehl: grammY entfernt die Leerzeichen davor und behält den Rest wie getippt, Zeilenumbrüche eingeschlossen. bot.catch() ist hier wichtig: Laut der Dokumentation von grammY stoppt ein Fehler in einem Handler einen mit bot.start()gestarteten Bot, wenn es keinen Fehlerhandler gibt, und ein Datenbankfehler ist genau so ein Fehler.

Die ? -Platzhalter schicken die Notiz getrennt vom SQL an die Datenbank, deshalb wird eine Notiz wie '); DROP TABLE notes; -- als normaler Text gespeichert. In unserem Test wurde sie als Zeile 2 gespeichert und die Tabelle blieb erhalten. Baue das SQL nie mit einem Template-String zusammen.

Telegram-IDs als BIGINT speichern

In der Bot API-Dokumentation von Telegram steht, eine Nutzer- oder Chat-ID „kann mehr als 32 signifikante Bits haben“, aber höchstens 52, deshalb ist eine 64-Bit-Ganzzahl sicher. Die Seite zu den IDs nennt die Bereiche: Nutzer-IDs gehen bis 1.099.511.627.775, Gruppen-, Supergruppen- und Kanal-IDs sind negativ. Supergruppen und Kanäle reichen von -1.000.000.000.001 bis -1.997.852.516.352 (zum Beispiel -1001234567890). Das INT von MySQL endet bei 2.147.483.647. In unserem Test, mit demselben strikten SQL-Modus wie auf unserem Datenbankserver, schlug das Speichern der Nutzer-ID 7.000.000.001 in einer INT -Spalte fehl. mysql2 lieferte den Fehlercode ER_WARN_DATA_OUT_OF_RANGE (errno 1264) mit dieser Meldung:

Out of range value for column 'user_id' at row 1

Eine BIGINT -Spalte hat sie gespeichert, und -1001234567890 auch. Nimm einfach BIGINT, nicht BIGINT UNSIGNED, damit auch Chat-IDs hineinpassen.

4. Starten

  1. Lade index.js und package.json unter Dateienhoch. Lade nicht node_moduleshoch.
  2. Drücke Starten und öffne Konsole. Beim Start werden die Pakete aus package.jsoninstalliert, dann erscheinen [PlayNHost] Starting your bot, Database ready und Logged in as @your_bot. Diese letzte Zeile schaltet den Status unter Übersicht auf Online in Telegram.
  3. Schick deinem Bot in Telegram /note buy milk, dann /notes. Um dir die Zeilen anzusehen, öffne die Seite der Datenbank und klicke auf In phpMyAdmin öffnen.

In unserem Test hat der Telegram-Ersatz diese Befehle gesendet und diese Antworten bekommen:

GesendetAntwort
/note buy milkSaved. Send /notes to see your last five.
/noteWrite your note after the command, for example: /note buy milk
/note und 501 ZeichenA note can be up to 500 characters.
/notesDie gespeicherten Notizen, neueste zuerst
/notes von einem zweiten NutzerYou have no notes yet.

Viele Nachrichten gleichzeitig

Laut der Dokumentation von grammY verarbeitet bot.start() alle Updates nacheinander: Ein Befehl ist fertig, bevor der nächste beginnt. In unserem Test hielt der Bot nach den Antworten auf die 8 Befehle 1 offene Verbindung. connectionLimit: 5 wird wichtig, sobald dein Code Abfragen parallel ausführt oder du auf grammY runner umsteigst, das Updates gleichzeitig verarbeitet: Der Pool öffnet nie mehr als 5 Verbindungen, und eine weitere Abfrage wartet auf eine freie.

Eine fehlgeschlagene Abfrage stoppt den Bot nicht

In unserem Test haben wir die Tabelle gelöscht, während der Bot lief, und /notesgesendet. bot.catch() gab Error while handling update 500001: Error: Table '...notes' doesn't exist aus, mit code: 'ER_NO_SUCH_TABLE', der Bot schickte für diesen Befehl keine Antwort und lief weiter: Sobald die Tabelle wieder da war, wurde das nächste /note gespeichert. Wer den Befehl geschickt hat, sieht nichts. Wenn eine Abfrage fehlschlagen kann, pack sie im Handler in try/catch und antworte mit einer kurzen Nachricht.

Eine ruhige Nacht bringt den Pool nicht kaputt

Unser Datenbankserver schließt eine Verbindung, die 8 Stunden lang ungenutzt war. Ein Bot, der so lange keine Nachrichten bekommt, zum Beispiel über Nacht, findet seine gepoolten Verbindungen geschlossen vor. Der Pool von mysql2 regelt das von selbst. Wir haben es mit auf 5 Sekunden gesenktem Leerlauflimit getestet: Nach 7 ruhigen Sekunden wurde das nächste /note ohne Fehler gespeichert.

Limits, die für deinen Pool wichtig sind

  • 20 Verbindungen gleichzeitig pro Datenbankbenutzer. Jeder Prozess mit demselben Login zählt mit. Verbindung 21 wird mit ER_USER_LIMIT_REACHEDabgelehnt. In unserem Test schlug ein Pool mit connectionLimit: 30 mit diesem Fehler fehl, als 30 Abfragen gleichzeitig liefen. Ein Pool mit 5 hält den Bot weit darunter.
  • Eine Abfrage, die länger als 120 Sekunden läuft, wird gestoppt. MariaDB beendet sie mit Fehler 1969, Query execution was interrupted (max_statement_time exceeded). In einem früheren Test von uns, auf einem genauso eingerichteten Konto, kam das nach 120,03 Sekunden. Die Abfragen eines Bots dauern normalerweise Millisekunden, deshalb trifft das 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 Fehlercodes, die mysql2 in unserem Test geliefert hat:

  • ER_ACCESS_DENIED_ERROR ... Access denied for user: falscher 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 nichts die Verbindung angenommen. Prüfe DB_HOST und DB_PORT. Auch ein fehlendes DB_HOST landet hier, weil mysql2 sich dann mit localhostverbindet, wo keine Datenbank läuft: Die Variable fehlt unter Umgebung, oder der Bot wurde nach dem Hinzufügen nicht neu gestartet.
  • ENOTFOUND: DB_HOST ist falsch geschrieben, der Name existiert also nicht.
  • ETIMEDOUT: Nichts hat rechtzeitig geantwortet. Egal, was die Meldung sagt: Eine Verbindung von deinem eigenen Computer schlägt fehl, denn die Datenbank akzeptiert nur Verbindungen von PlayNHost-Servern und phpMyAdmin.
  • ER_USER_LIMIT_REACHED ... has exceeded the 'max_user_connections' resource (current value: 20): zu viele offene Verbindungen, siehe die Limits oben. Zum Beispiel eine zweite Kopie des Bots oder ein Pool, der für jede Nachricht neu erstellt wird.
  • ER_WARN_DATA_OUT_OF_RANGE (errno 1264, Out of range value for column 'user_id' at row 1): Eine ID-Spalte ist INT. Ändere sie auf BIGINT.
  • ER_NO_SUCH_TABLE (errno 1146): Die Tabelle fehlt. Der Code oben legt sie beim Start an; wenn du sie von Hand erstellt hast, prüfe ihren Namen.

Ein falsches Token fällt erst nach dem Datenbankteil auf. Mit unserem falschen Token gegen das echte Telegram gab der Bot Database readyaus und stoppte dann nach etwa einer Viertelsekunde mit GrammyError: Call to 'deleteWebhook' failed! (401: Unauthorized). Kopiere das Token erneut von @BotFather und füge es auf der Seite Bot im Bereich Telegramein. Mehr Telegram-Fehler: Telegram-Bot-Fehler 409 Conflict beheben.

Wie viel Speicher er braucht

In unserem Test belegte der laufende Bot nach den Antworten auf die 8 Befehle 67,6 MB Speicher (resident), mit offenem Pool, in einem auf 512 MB begrenzten Container wie bei unserem kleinsten Bot-Tarif. In unserem früheren Hosting-Test maß grammY allein 60 MB (dieser Bot stoppte bei einem abgelehnten Token, bevor er eine Nachricht verarbeitet hatte), der Unterschied von etwa 8 MB deckt also mysql2, den Pool und die verarbeiteten Befehle ab. Die Installation von grammY und mysql2 mit npm von null auf erreichte 140 MB Spitze, Dateicache eingeschlossen.

Dasselbe mit einer anderen Bibliothek

Brauchst du einen Server für deinen Telegram-Bot mit Datenbank? Die Telegram-Bot-Tarife ansehen.

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

UNSERE NEUESTEN BLOGS