Ce qu'il vous faut
- Un bot discord.js écrit en TypeScript. Notre type Node.js Bot exécute avec tsx un fichier principal se terminant par
.ts, donc rien n'a besoin d'être compilé avant : Comment faire tourner un bot Discord TypeScript.
- Une offre de bot PlayNHost avec le type Node.js Bot. Chaque offre comprend une base de données.
- Node.js et npm sur votre propre ordinateur, pour écrire la migration une fois.
1. Créer la base de données et les variables
- Ouvrez le serveur de votre bot dans le panel, cliquez sur Bases de données, puis sur Nouvelle base de données et Créer une base de données.
- La page de la nouvelle base s'ouvre (plus tard, cliquez dessus dans Vos bases de données). Gardez-la ouverte et, sous Config prête à l’emploi, choisissez Generic URL dans Config pour : c'est la chaîne de connexion
mysql://... dont la ligne de commande Prisma a besoin.
- Ouvrez la page Bot, le volet Environnement, cliquez sur Coller un fichier .env et collez six lignes avec vos valeurs. Pour
DATABASE_URL, collez la Generic URL exactement telle qu'affichée : les mots de passe de nos bases peuvent contenir des caractères comme @ et +, que Prisma exige encodés en pourcentage dans une URL, et la Generic URL est déjà encodée.
DB_HOST=your-host
DB_PORT=3306
DB_NAME=your-database
DB_USER=your-username
DB_PASSWORD=your-password
DATABASE_URL=mysql://your-username:your-password@your-host:3306/your-database
DB_PASSWORD et DATABASE_URL reçoivent un badge Secret : ils sont enregistrés en écriture seule. Cliquez sur Importer 6 variables.
Le bot lui-même se connecte avec les cinq valeurs DB_, transmises une par une à l'adaptateur comme dans le quickstart de Prisma. DATABASE_URL ne sert qu'aux commandes prisma exécutées pendant l'installation.
2. Les fichiers du projet
package.json
{
"name": "my-discord-bot",
"private": true,
"type": "module",
"scripts": {
"postinstall": "prisma generate && prisma migrate deploy"
},
"dependencies": {
"@prisma/adapter-mariadb": "^7",
"@prisma/client": "^7",
"discord.js": "^14"
},
"devDependencies": {
"prisma": "^7"
}
}
prisma7.config.ts (Prisma 7.10 et les versions suivantes utilisent ce nom, pour ne pas entrer en conflit avec le prisma.config.ts de Prisma 8)
import { defineConfig } from "prisma/config";
export default defineConfig({
schema: "prisma/schema.prisma",
migrations: {
path: "prisma/migrations",
},
datasource: {
url: process.env.DATABASE_URL,
},
});
prisma/schema.prisma
generator client {
provider = "prisma-client"
output = "../generated/prisma"
}
datasource db {
provider = "mysql"
}
model Warning {
id Int @id @default(autoincrement())
guildId String @db.VarChar(20)
userId String @db.VarChar(20)
reason String @db.VarChar(200)
createdAt DateTime @default(now())
@@index([guildId, userId])
}
index.ts
import { Client, Events, GatewayIntentBits, InteractionContextType, MessageFlags, REST, Routes, SlashCommandBuilder } from "discord.js";
import { PrismaMariaDb } from "@prisma/adapter-mariadb";
import { PrismaClient } from "./generated/prisma/client";
// One client for the whole bot. The values come from the Environment pane.
const adapter = new PrismaMariaDb({
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 prisma = new PrismaClient({ adapter });
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 (c) => {
await new REST().setToken(process.env.DISCORD_TOKEN!).put(Routes.applicationCommands(c.user.id), { body: commands });
console.log(`Logged in as ${c.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 prisma.warning.create({ data: { guildId: interaction.guildId, userId: user.id, reason } });
const total = await prisma.warning.count({ where: { guildId: interaction.guildId, userId: user.id } });
await interaction.reply(`${user} has ${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);
}
});
client.login(process.env.DISCORD_TOKEN);
setContexts(InteractionContextType.Guild) et la vérification !interaction.guildId empêchent /warn de fonctionner en message privé. Le try/catch est important : sans lui, discord.js transmet une requête échouée à son événement error, et quand personne n'y écoute, Node.js arrête le bot.
3. Écrire la première migration sur votre ordinateur
Dans le dossier du projet sur votre propre ordinateur (sous Windows, créez à la main les dossiers prisma\migrations\0_init au lieu d'utiliser mkdir -p) :
npm install --ignore-scripts
mkdir -p prisma/migrations/0_init
npx prisma migrate diff --from-empty --to-schema prisma/schema.prisma --script > prisma/migrations/0_init/migration.sql
migrate diff compare une base vide avec votre schéma et écrit le SQL, sans se connecter à aucune base de données. --ignore-scripts ignore le postinstall, qui essaierait de joindre une base. Le fichier commence par -- CreateTable et CREATE TABLE `Warning`.
4. Téléverser et démarrer
- Dans Fichiers, téléversez
package.json, prisma7.config.ts, index.ts et le dossier prisma avec schema.prisma et migrations. Ne téléversez pas node_modules ni generated : le serveur construit les deux.
- Sur la page Bot, volet Code, réglez Fichier d’entrée sur
index.ts.
- Appuyez sur Démarrer et ouvrez Console. L'installation exécute votre
postinstall : vous voyez Generated Prisma Client (7.10.1), puis All migrations have been successfully applied., puis [PlayNHost] Starting your bot et Logged in as ....
La console affiche aussi npm warn install-scripts 2 packages had install scripts blocked pour prisma et @prisma/engines. La version de npm de notre image Node.js 22 bloque les scripts d'installation des dépendances tant qu'ils ne sont pas autorisés. Votre propre postinstall s'exécute quand même, et dans notre test Prisma a généré, migré et interrogé sans ces scripts.
Pourquoi pas prisma migrate dev ?
prisma migrate dev crée d'abord une base shadow temporaire pour vérifier vos migrations. L'utilisateur de la base de votre bot peut travailler dans sa propre base mais ne peut pas en créer de nouvelles, donc sur nos serveurs la commande s'arrête avec :
Prisma Migrate could not create the shadow database. Please make sure the database user has permission to create databases. Read more about the shadow database (and workarounds) at https://pris.ly/d/migrate-shadow
Original error: Error code: P1010
User was denied access on the database `prisma_migrate_shadow_db_...`
prisma migrate deploy applique seulement les fichiers de migration et n'a pas besoin de base shadow, c'est pourquoi le postinstall l'utilise. Vous ne pouvez pas non plus pointer migrate dev vers la base du bot depuis votre propre ordinateur : la base n'accepte que les connexions des serveurs PlayNHost et de phpMyAdmin.
Modifier le schéma plus tard
- Sur votre ordinateur, lancez
npx prisma migrate dev --name suivi d'un nom pour la modification, sur un MySQL ou MariaDB à vous, par exemple dans Docker : définissez DATABASE_URL dans votre terminal sur cette base, car prisma7.config.ts la lit dans l'environnement. Cela écrit un nouveau dossier dans prisma/migrations.
- Téléversez le nouveau dossier de migration et le
schema.prisma modifié.
- Le serveur exécute
npm install, et avec lui votre postinstall, seulement quand package.json, package-lock.json ou la version de Node.js ont changé, quand node_modules est absent, ou au premier démarrage plus d'un jour après la dernière installation. Pour appliquer la migration tout de suite, modifiez package.json (par exemple son version) ou supprimez le dossier node_modules dans Fichiers, puis redémarrez.
Mémoire : l'installation est la partie lourde
Nous avons installé ce projet de zéro dans un conteneur plafonné à 512 Mo, comme notre plus petite offre de bot. L'installation, avec prisma generate et prisma migrate deploy, a pris 41 secondes et a atteint un pic de 386 Mo de mémoire (cache de fichiers non compté) lors d'une exécution et de 417 Mo lors d'une seconde, sans arrêt pour manque de mémoire dans aucune des deux. Elle tient dans l'offre de 512 Mo avec moins de 100 Mo de marge, donc un projet plus gros ou plus de paquets pourraient dépasser. La même installation sans Prisma (discord.js et mysql2 seulement) a atteint un pic de 147 Mo.
Limites et erreurs
- 20 connexions simultanées par utilisateur de base de données, tous les processus utilisant le même identifiant confondus. Gardez
connectionLimit bas ; 5 suffisent largement pour un bot.
- Une requête qui dure plus de 120 secondes est arrêtée, avec l'erreur 1969
Query execution was interrupted (max_statement_time exceeded).
- 500 Mo par base de données : à 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 problèmes de connexion ont les mêmes causes qu'avec mysql2 (mauvais mot de passe, mauvais hôte ou port, une variable absente dans Environnement, connexion depuis votre propre ordinateur), mais les messages sont différents : l'adaptateur MariaDB utilise le pilote
mariadb, pas mysql2, et nous n'avons pas relevé ses messages. Les causes sont listées dans Comment utiliser une base de données MySQL avec un bot discord.js.
Du SQL simple avec mysql2 ou aiomysql à la place : discord.js (mysql2), discord.py (aiomysql). Le panorama : Comment utiliser une base de données avec votre bot Discord. Besoin d'un serveur de bot avec base de données incluse ? Voir les offres de bot.