NIEUW 📢 Beperkte lanceeractie: tot 70% korting op je eerste maand, voor elke game!Bekijk pakketten

Een MySQL-database gebruiken met een grammY-bot (mysql2)

Telegram-bothostingUpdated Nov 6, 202611 min read

Kort antwoord. Voeg mysql2 toe aan je grammY-bot, maak bij het starten van de bot één verbindingspool aan uit omgevingsvariabelen, maak je tabellen aan vóór bot.start()en laat elke query via de pool lopen. Gebruik ? -placeholders en sla Telegram-ID's op als BIGINT, niet als INT. Bij PlayNHost bevat elk botabonnement één MySQL-database: maak hem aan op de pagina Databases , plak de gegevens in het deel Omgeving en de code hieronder werkt zoals hij is. De database accepteert verbindingen van je PlayNHost-servers en van phpMyAdmin, niet van je eigen computer. We hebben deze code getest op 6 november 2026 met grammY 1.46.0, mysql2 3.24.6 en Node.js 22, tegen MariaDB 10.11 met een databaseaccount dat is ingesteld zoals het onze (20 verbindingen, querylimiet van 120 seconden) en een vervanger voor de Bot API-server van Telegram: 8 commando's erin, 8 antwoorden eruit, de notities opgeslagen in de database. Een echte Telegram-login maakte geen deel uit van de test.

Wat je nodig hebt

1. De database aanmaken

  1. Open je botserver in het paneel en klik op Databases.
  2. Klik op Nieuwe database. Een Weergavenaam is optioneel; Wordt aangemaakt als toont de volledige naam. Klik op Database aanmaken.
  3. De pagina van de nieuwe database opent (later klik je hem aan onder Jouw databases). De kaart Verbinding toont Host, Poort, Database, Gebruikersnaam en Wachtwoord. Onder Kant-en-klare config, Config voor bevat een fragment Node.js (mysql2) met dezelfde waarden al ingevuld.

2. De gegevens in Omgeving zetten

  1. Open de pagina Bot , deel Omgevingen klik op Een .env-bestand plakken.
  2. Plak deze vijf regels met de waarden van de kaart Verbinding :
    DB_HOST=your-host
    DB_PORT=3306
    DB_NAME=your-database
    DB_USER=your-username
    DB_PASSWORD=your-password
  3. DB_PASSWORD krijgt een badge Geheim : hij wordt alleen-schrijven opgeslagen en nooit meer getoond. Klik op 5 variabelen importeren.

Gebruik de echte poort van de kaart Verbinding . De bottoken staat niet in deze regels: de token die je hebt opgeslagen op de pagina Bot , deel Telegram, komt al in je code aan als TELEGRAM_BOT_TOKEN, de naam die de code hieronder leest (en als BOT_TOKEN, TOKEN, TELEGRAM_TOKEN en TELOXIDE_TOKEN). Als je .env -bestand een tokenregel heeft, slaat het dialoogvenster die over. Meer over dit deel: Omgevingsvariabelen en geheimen instellen voor je Discord-bot.

3. De code

Een kleine notitiebot: /note buy milk slaat een notitie op, /notes toont je laatste vijf.

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);
});

Wat hij doet: main() maakt de tabel notes aan als die ontbreekt en print Database ready voordat bot.start() Telegram om berichten vraagt. De pool wordt één keer aangemaakt, bovenaan het bestand, en elk commando gebruikt hem: pool.execute() leent een verbinding, voert de query uit en geeft de verbinding terug. ctx.match is de tekst na het commando: grammY haalt de spaties ervoor weg en houdt de rest zoals getypt, inclusief regeleinden. bot.catch() is hier belangrijk: volgens de documentatie van grammY stopt een fout in een handler, zonder foutafhandelaar, een bot die met bot.start()is gestart, en een databasefout is precies zo'n fout.

De ? -placeholders sturen de notitie los van de SQL naar de database, dus een notitie zoals '); DROP TABLE notes; -- wordt als gewone tekst opgeslagen. In onze test werd hij opgeslagen als rij 2 en bleef de tabel bestaan. Bouw de SQL nooit op met een template-string.

Telegram-ID's opslaan als BIGINT

In de Bot API-documentatie van Telegram staat dat een gebruikers- of chat-ID „meer dan 32 significante bits kan hebben”, maar hoogstens 52, dus een 64-bits geheel getal is veilig. De pagina over ID's geeft de bereiken: gebruikers-ID's gaan tot 1.099.511.627.775, terwijl ID's van groepen, supergroepen en kanalen negatief zijn. Supergroepen en kanalen lopen van -1.000.000.000.001 tot -1.997.852.516.352 (bijvoorbeeld -1001234567890). INT van MySQL stopt bij 2.147.483.647. In onze test, met dezelfde strikte SQL-modus die onze databaseserver gebruikt, mislukte het opslaan van de gebruikers-ID 7.000.000.001 in een INT -kolom. mysql2 gaf de foutcode ER_WARN_DATA_OUT_OF_RANGE (errno 1264) met deze melding:

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

Een BIGINT -kolom sloeg hem wel op, en -1001234567890 ook. Gebruik gewone BIGINT, niet BIGINT UNSIGNED, zodat ook chat-ID's passen.

4. Starten

  1. Upload index.js en package.json bij Files. Upload node_modulesniet.
  2. Druk op Starten en open Console. Bij het starten worden de pakketten uit package.jsongeĂŻnstalleerd, daarna zie je [PlayNHost] Starting your bot, Database ready en Logged in as @your_bot. Die laatste regel zet de status onder Overzicht op Online in Telegram.
  3. Stuur je bot in Telegram /note buy milk, en daarna /notes. Om de rijen te bekijken, open je de pagina van de database en klik je op Openen in phpMyAdmin.

In onze test stuurde de Telegram-vervanger deze commando's en kreeg deze antwoorden:

VerzondenAntwoord
/note buy milkSaved. Send /notes to see your last five.
/noteWrite your note after the command, for example: /note buy milk
/note en 501 tekensA note can be up to 500 characters.
/notesDe opgeslagen notities, nieuwste eerst
/notes van een tweede gebruikerYou have no notes yet.

Veel berichten tegelijk

Volgens de documentatie van grammY verwerkt bot.start() alle updates na elkaar: een commando is klaar voordat het volgende begint. In onze test hield de bot 1 open verbinding na het beantwoorden van de 8 commando's. connectionLimit: 5 doet ertoe zodra je code queries parallel uitvoert, of als je overstapt naar grammY runner, dat updates gelijktijdig verwerkt: de pool opent nooit meer dan 5 verbindingen, en een extra query wacht op een vrije.

Een mislukte query stopt de bot niet

In onze test hebben we de tabel verwijderd terwijl de bot draaide en /notesgestuurd. bot.catch() printte Error while handling update 500001: Error: Table '...notes' doesn't exist met code: 'ER_NO_SUCH_TABLE', de bot stuurde geen antwoord op dat commando en bleef draaien: toen de tabel terug was, werd de volgende /note opgeslagen. Wie het commando stuurde, ziet niets, dus als een query kan mislukken, zet hem in de handler in try/catch en antwoord met een kort bericht.

Een rustige nacht breekt de pool niet

Onze databaseserver sluit een verbinding die 8 uur ongebruikt is geweest. Een bot die zo lang geen berichten krijgt, bijvoorbeeld 's nachts, vindt zijn gepoolde verbindingen gesloten. De pool van mysql2 lost dat zelf op. We testten het met de inactiviteitslimiet verlaagd naar 5 seconden: na 7 rustige seconden werd de volgende /note zonder fout opgeslagen.

Limieten die voor je pool belangrijk zijn

  • 20 verbindingen tegelijk per databasegebruiker. Elk proces dat dezelfde login gebruikt telt mee. Verbinding 21 wordt geweigerd met ER_USER_LIMIT_REACHED. In onze test mislukte een pool met connectionLimit: 30 met die fout toen 30 queries tegelijk liepen. Een pool van 5 houdt de bot ruim eronder.
  • Een query die langer dan 120 seconden draait, wordt gestopt. MariaDB beĂ«indigt hem met fout 1969, Query execution was interrupted (max_statement_time exceeded). In een eerdere test van ons, op een account dat op dezelfde manier is ingesteld, gebeurde dat na 120,03 seconden. De queries van een bot duren normaal milliseconden, dus dit raakt alleen een op hol geslagen query.
  • 500 MB per database. De pagina Databases toont hoeveel er gebruikt wordt. Bij 500 MB worden schrijfacties gestopt tot je rijen verwijdert.
  • Serverback-ups bevatten de database niet. Exporteer hem af en toe uit phpMyAdmin.

Fouten en wat ze betekenen

Dit zijn de foutcodes die mysql2 in onze test gaf:

  • ER_ACCESS_DENIED_ERROR ... Access denied for user: verkeerde DB_USER of DB_PASSWORD. Na Wachtwoord resetten op de kaart Verbinding werkt het oude wachtwoord meteen niet meer: vervang DB_PASSWORD en start de bot opnieuw.
  • ECONNREFUSED: niets accepteerde de verbinding op die host en poort. Controleer DB_HOST en DB_PORT. Ook een ontbrekende DB_HOST komt hier uit, omdat mysql2 dan verbinding maakt met localhost, waar geen database draait: de variabele ontbreekt in Omgeving, of de bot is na het toevoegen niet opnieuw gestart.
  • ENOTFOUND: DB_HOST is verkeerd gespeld, dus de naam bestaat niet.
  • ETIMEDOUT: niets antwoordde op tijd. Wat de melding ook zegt, een verbinding vanaf je eigen computer mislukt: de database accepteert alleen verbindingen van PlayNHost-servers en phpMyAdmin.
  • ER_USER_LIMIT_REACHED ... has exceeded the 'max_user_connections' resource (current value: 20): te veel open verbindingen, zie de limieten hierboven. Bijvoorbeeld een tweede kopie van de bot, of een pool die voor elk bericht wordt aangemaakt.
  • ER_WARN_DATA_OUT_OF_RANGE (errno 1264, Out of range value for column 'user_id' at row 1): een ID-kolom is INT. Verander hem in BIGINT.
  • ER_NO_SUCH_TABLE (errno 1146): de tabel ontbreekt. De code hierboven maakt hem bij het starten aan; als je hem met de hand hebt gemaakt, controleer dan de naam.

Een verkeerde token valt pas na het databasegedeelte op. Met onze nepbottoken tegen het echte Telegram printte de bot Database readyen stopte daarna na ongeveer een kwart seconde met GrammyError: Call to 'deleteWebhook' failed! (401: Unauthorized). Kopieer de token opnieuw van @BotFather en plak hem op de pagina Bot , deel Telegram. Meer Telegram-fouten: Telegram-bot 409 Conflict oplossen.

Hoeveel geheugen hij gebruikt

In onze test gebruikte de draaiende bot 67,6 MB geheugen (resident) na het beantwoorden van de 8 commando's, met geopende pool, in een container begrensd op 512 MB zoals ons kleinste botabonnement. In onze eerdere hostingtest mat grammY alleen 60 MB (die bot stopte bij een geweigerde token, voordat hij een bericht verwerkte), dus het verschil, ongeveer 8 MB, dekt mysql2, de pool en de verwerkte commando's. grammY en mysql2 met npm installeren vanaf nul piekte op 140 MB, bestandscache inbegrepen.

Hetzelfde met een andere bibliotheek

Een server nodig voor je Telegram-bot met een database inbegrepen? Bekijk de Telegram-botabonnementen.

Draait je bot al ergens anders? Verhuis hem naar ons

ONZE NIEUWSTE BLOGS