NOUVEAU 📢 Offre de lancement limitée : jusqu'à 70 % de remise sur votre premier mois, pour tous les jeux !Voir les offres

Comment utiliser une base de données MySQL avec un bot grammY (mysql2)

Hébergement de bots TelegramUpdated Nov 6, 202612 min read

Réponse courte. Ajoutez mysql2 à votre bot grammY, créez un pool de connexions à partir des variables d’environnement au démarrage du bot, créez vos tables avant bot.start(), et faites passer chaque requête par le pool. Utilisez des paramètres ? et stockez les ID Telegram en BIGINT, pas en INT. Chez PlayNHost, chaque offre de bot inclut une base de données MySQL : créez-la sur la page Bases de données , collez ses informations dans le volet Environnement , et le code ci-dessous fonctionne tel quel. La base de données accepte les connexions de vos serveurs PlayNHost et de phpMyAdmin, pas de votre propre ordinateur. Nous avons testé ce code le 6 novembre 2026 avec grammY 1.46.0, mysql2 3.24.6 et Node.js 22, sur MariaDB 10.11 avec un compte de base de données configuré comme le nôtre (20 connexions, limite de requête de 120 secondes) et un substitut du serveur Bot API de Telegram : 8 commandes en entrée, 8 réponses en sortie, les notes enregistrées dans la base de données. Une vraie connexion à Telegram ne faisait pas partie du test.

Ce qu’il vous faut

1. Créer la base de données

  1. Ouvrez votre serveur de bot dans le panel et cliquez sur Bases de données.
  2. Cliquez sur Nouvelle base de données. Un Libellé est facultatif ; Sera créée sous le nom affiche le nom complet. Cliquez sur Créer une base de données.
  3. La page de la nouvelle base de données s’ouvre (plus tard, cliquez dessus dans Vos bases de données). Sa carte Connexion affiche Hôte, Port, Base de données, Nom d'utilisateur et Mot de passe. Sous Config prête à l’emploi, Config pour propose un extrait Node.js (mysql2) avec les mêmes valeurs déjà remplies.

2. Mettre les informations dans Environnement

  1. Ouvrez la page Bot , volet Environnement, et cliquez sur Coller un fichier .env.
  2. Collez ces cinq lignes avec les valeurs de la carte Connexion  :
    DB_HOST=your-host
    DB_PORT=3306
    DB_NAME=your-database
    DB_USER=your-username
    DB_PASSWORD=your-password
  3. DB_PASSWORD reçoit un badge Secret  : il est enregistré en écriture seule et n’est plus jamais affiché. Cliquez sur Importer 5 variables.

Utilisez le vrai port de la carte Connexion . Le token du bot ne fait pas partie de ces lignes : le token que vous avez enregistré sur la page Bot , volet Telegram, arrive déjà dans votre code sous le nom TELEGRAM_BOT_TOKEN, celui que lit le code ci-dessous (et sous les noms BOT_TOKEN, TOKEN, TELEGRAM_TOKEN et TELOXIDE_TOKEN). Si votre fichier .env contient une ligne de token, la boîte de dialogue l’ignore. Plus de détails sur ce volet : Comment définir des variables d’environnement et des secrets pour votre bot Discord.

3. Le code

Un petit bot de notes : /note buy milk enregistre une note, /notes affiche vos cinq dernières.

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

Ce qu’il fait : main() crée la table notes si elle n’existe pas et affiche Database ready avant que bot.start() ne demande les messages à Telegram. Le pool est créé une seule fois, en haut du fichier, et chaque commande l’utilise : pool.execute() emprunte une connexion, exécute la requête et rend la connexion. ctx.match est le texte après la commande : grammY retire les espaces qui le précèdent et garde le reste tel qu’il a été saisi, retours à la ligne compris. bot.catch() est important ici : selon la documentation de grammY, sans gestionnaire d’erreurs, une erreur dans un gestionnaire arrête un bot démarré avec bot.start(), et une erreur de base de données est exactement ce genre d’erreur.

Les paramètres ? envoient la note à la base de données séparément du SQL, donc une note comme '); DROP TABLE notes; -- est stockée comme du texte brut. Dans notre test, elle a été enregistrée à la ligne 2 et la table est restée. Ne construisez jamais le SQL avec une chaîne de gabarit.

Stocker les ID Telegram en BIGINT

La documentation de la Bot API de Telegram indique qu’un ID d’utilisateur ou de discussion « peut avoir plus de 32 bits significatifs », mais au plus 52, donc un entier de 64 bits suffit. Sa page sur les ID donne les plages : les ID d’utilisateur vont jusqu’à 1.099.511.627.775, alors que les ID de groupes, de supergroupes et de canaux sont négatifs. Les supergroupes et canaux vont de -1.000.000.000.001 à -1.997.852.516.352 (par exemple -1001234567890). Le type INT de MySQL s’arrête à 2.147.483.647. Dans notre test, avec le même mode SQL strict que notre serveur de base de données, l’enregistrement de l’ID d’utilisateur 7.000.000.001 dans une colonne INT a échoué. mysql2 a renvoyé le code d’erreur ER_WARN_DATA_OUT_OF_RANGE (errno 1264) avec ce message :

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

Une colonne BIGINT l’a enregistré, ainsi que -1001234567890 . Utilisez BIGINTsimple, pas BIGINT UNSIGNED, pour que les ID de discussion tiennent aussi.

4. Le lancer

  1. Envoyez index.js et package.json dans Fichiers. N’envoyez pas node_modules.
  2. Appuyez sur Démarrer et ouvrez Console. Au démarrage, les paquets de package.jsonsont installés, puis s’affichent [PlayNHost] Starting your bot, Database ready et Logged in as @your_bot. Cette dernière ligne fait passer l’état sous Aperçu à En ligne sur Telegram.
  3. Dans Telegram, envoyez à votre bot /note buy milk, puis /notes. Pour voir les lignes, ouvrez la page de la base de données et cliquez sur Ouvrir dans phpMyAdmin.

Dans notre test, le substitut de Telegram a envoyé ces commandes et reçu ces réponses :

EnvoyéRéponse
/note buy milkSaved. Send /notes to see your last five.
/noteWrite your note after the command, for example: /note buy milk
/note et 501 caractèresA note can be up to 500 characters.
/notesLes notes enregistrées, les plus récentes d’abord
/notes d’un deuxième utilisateurYou have no notes yet.

Beaucoup de messages à la fois

La documentation de grammY indique que bot.start() traite toutes les mises à jour séquentiellement : une commande se termine avant que la suivante ne commence. Dans notre test, le bot avait 1 connexion ouverte après avoir répondu aux 8 commandes. connectionLimit: 5 compte dès que votre code exécute des requêtes en parallèle, ou si vous passez à grammY runner, qui traite les mises à jour en parallèle : le pool n’ouvre jamais plus de 5 connexions, et une requête supplémentaire attend qu’une connexion se libère.

Une requête qui échoue n’arrête pas le bot

Dans notre test, nous avons supprimé la table pendant que le bot tournait et envoyé /notes. bot.catch() a affiché Error while handling update 500001: Error: Table '...notes' doesn't exist avec code: 'ER_NO_SUCH_TABLE', le bot n’a envoyé aucune réponse pour cette commande et a continué de tourner : une fois la table revenue, le /note suivant a été enregistré. La personne qui a envoyé la commande ne voit rien, donc si une requête peut échouer, entourez-la de try/catch dans le gestionnaire et répondez par un court message.

Une nuit calme ne casse pas le pool

Notre serveur de base de données ferme une connexion restée inactive pendant 8 heures. Un bot qui ne reçoit aucun message aussi longtemps, pendant la nuit par exemple, trouve ses connexions du pool fermées. Le pool de mysql2 s’en charge tout seul. Nous l’avons testé avec la limite d’inactivité abaissée à 5 secondes : après 7 secondes de calme, le /note suivant a été enregistré sans erreur.

Limites qui comptent pour votre pool

  • 20 connexions à la fois par utilisateur de base de données. Chaque processus qui utilise le même identifiant compte. La connexion 21 est refusée avec ER_USER_LIMIT_REACHED. Dans notre test, un pool avec connectionLimit: 30 a échoué avec cette erreur quand 30 requêtes se sont exécutées en même temps. Un pool de 5 garde le bot bien en dessous.
  • Une requête qui dure plus de 120 secondes est arrêtée. MariaDB l’interrompt avec l’erreur 1969, Query execution was interrupted (max_statement_time exceeded). Dans un test précédent, sur un compte configuré de la même façon, cela est arrivé après 120,03 secondes. Les requêtes d’un bot prennent normalement quelques millisecondes, donc cela ne concerne qu’une requête qui s’emballe.
  • 500 Mo par base de données. La page Bases de données montre l’espace utilisé. À 500 Mo, les écritures sont bloquées jusqu’à ce que vous supprimiez des lignes.
  • Les sauvegardes du serveur n’incluent pas la base de données. Exportez-la de temps en temps depuis phpMyAdmin.

Les erreurs et leur signification

Voici les codes d’erreur renvoyés par mysql2 dans notre test :

  • ER_ACCESS_DENIED_ERROR ... Access denied for user : DB_USER ou DB_PASSWORDincorrect. Après Réinitialiser le mot de passe sur la carte Connexion , l’ancien mot de passe cesse de fonctionner immédiatement : remplacez DB_PASSWORD et redémarrez le bot.
  • ECONNREFUSED : rien n’a accepté la connexion à cet hôte et ce port. Vérifiez DB_HOST et DB_PORT. Un DB_HOST absent mène aussi ici, car mysql2 se connecte alors à localhost, où aucune base de données ne tourne : la variable manque dans Environnement, ou le bot n’a pas été redémarré après son ajout.
  • ENOTFOUND : DB_HOST est mal orthographié, donc le nom n’existe pas.
  • ETIMEDOUT : rien n’a répondu à temps. Quel que soit le message, une connexion depuis votre propre ordinateur échoue : la base de données n’accepte que les connexions des serveurs PlayNHost et de phpMyAdmin.
  • ER_USER_LIMIT_REACHED ... has exceeded the 'max_user_connections' resource (current value: 20) : trop de connexions ouvertes, voir les limites ci-dessus. Par exemple une deuxième copie du bot, ou un pool créé pour chaque message.
  • ER_WARN_DATA_OUT_OF_RANGE (errno 1264, Out of range value for column 'user_id' at row 1) : une colonne d’ID est en INT. Changez-la en BIGINT.
  • ER_NO_SUCH_TABLE (errno 1146) : la table est absente. Le code ci-dessus la crée au démarrage ; si vous l’avez créée à la main, vérifiez son nom.

Un mauvais token se voit après la partie base de données. Avec notre faux token contre le vrai Telegram, le bot a affiché Database ready, puis s’est arrêté au bout d’environ un quart de seconde avec GrammyError: Call to 'deleteWebhook' failed! (401: Unauthorized). Copiez à nouveau le token depuis @BotFather et collez-le sur la page Bot , volet Telegram. Plus d’erreurs Telegram : Corriger l’erreur 409 Conflict d’un bot Telegram.

Combien de mémoire il utilise

Dans notre test, le bot en marche a utilisé 67,6 Mo de mémoire (résidente) après avoir répondu aux 8 commandes, avec son pool ouvert, dans un conteneur plafonné à 512 Mo comme notre plus petite offre de bot. Dans notre test d’hébergement précédent, grammY seul mesurait 60 Mo (ce bot s’était arrêté sur un token refusé, avant de traiter le moindre message), donc la différence, environ 8 Mo, couvre mysql2, le pool et les commandes traitées. L’installation de grammY et mysql2 avec npm à partir de zéro a atteint un pic de 140 Mo, cache de fichiers compris.

La même chose avec une autre bibliothèque

Besoin d’un serveur pour votre bot Telegram avec une base de données incluse ? Voir les offres de bots Telegram.

Votre bot est hébergé ailleurs ? Transférez-le chez nous

NOS DERNIERS ARTICLES