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

Prisma met MySQL gebruiken in een Discord-bot

Discord-bothostingUpdated Nov 6, 20269 min read

Kort antwoord. Gebruik Prisma 7 met de driveradapter @prisma/adapter-mariadb, die Prisma documenteert voor MySQL en MariaDB. Bij PlayNHost kan prisma migrate dev niet draaien op de database van je bot, omdat het een tijdelijke "shadow"-database moet aanmaken en databasegebruikers hier geen databases kunnen aanmaken. Schrijf de eerste migratie in plaats daarvan met prisma migrate diff, dat geen database nodig heeft, en laat een postinstall-script prisma generate en prisma migrate deploy op de server uitvoeren. Elk botabonnement bevat één MySQL-database. We hebben de code en opdrachten hieronder getest op 6 november 2026 met Prisma 7.10.1, discord.js 14 en Node.js 22, tegen MariaDB 10.11 met een databaseaccount dat is ingesteld zoals het onze (geen recht om databases aan te maken, 20 verbindingen, querylimiet van 120 seconden), maar niet met een echte Discord-login.

Prisma 7, niet de nieuwste npm-versie

Op 6 november 2026 wijst de tag latest van het pakket prisma op npm naar een release candidate van Prisma 8 (8.0.0-rc.30), en de MySQL-quickstart van Prisma zelf is die van Prisma 7: hij installeert prisma@prev en @prisma/client@7. Daarom zet deze gids elk Prisma-pakket vast op ^7. Een gewone npm install prisma zou je versie 8 geven.

Wat je nodig hebt

  • Een discord.js-bot die in TypeScript is geschreven. Ons bottype Node.js Bot draait met tsx een hoofdbestand dat eindigt op .ts, dus er hoeft niets eerst te worden gecompileerd: Een TypeScript Discord-bot draaien.
  • Een PlayNHost-botabonnement met het type Node.js Bot. Elk abonnement bevat één database.
  • Node.js en npm op je eigen computer, om de migratie één keer te schrijven.

1. Maak de database en de variabelen aan

  1. Open je botserver in het paneel, klik op Databases, daarna op Nieuwe database en Database aanmaken.
  2. De pagina van de nieuwe database opent (later klik je erop onder Jouw databases). Laat hem open en kies onder Kant-en-klare config bij Config voor de optie Generic URL: dat is de mysql://...-verbindingsstring die de Prisma-opdrachtregel nodig heeft.
  3. Open de pagina Bot, het paneel Omgeving, klik op Een .env-bestand plakken en plak zes regels met je waarden. Plak voor DATABASE_URL de Generic URL precies zoals hij wordt getoond: onze databasewachtwoorden kunnen tekens bevatten zoals @ en +, die Prisma procentgecodeerd nodig heeft in een URL, en de Generic URL is al gecodeerd.
    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
  4. DB_PASSWORD en DATABASE_URL krijgen een Geheim-badge: ze worden alleen-schrijven opgeslagen. Klik op 6 variabelen importeren.

De bot zelf verbindt met de vijf DB_-waarden, die één voor één aan de adapter worden doorgegeven zoals in de quickstart van Prisma. DATABASE_URL is alleen voor de prisma-opdrachten die tijdens de installatie draaien.

2. De projectbestanden

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 en nieuwer gebruiken deze naam, zodat hij niet botst met prisma.config.ts van 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) en de controle !interaction.guildId houden /warn uit privéberichten. De try/catch is belangrijk: zonder die geeft discord.js een mislukte query door aan zijn error-event, en als daar niemand luistert, stopt Node.js de bot.

3. Schrijf de eerste migratie op je computer

Voer in de projectmap op je eigen computer het volgende uit (maak op Windows de mappen prisma\migrations\0_init met de hand aan in plaats van 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 vergelijkt een lege database met je schema en schrijft de SQL, zonder met een database te verbinden. --ignore-scripts slaat de postinstall over, die een database zou proberen te bereiken. Het bestand begint met -- CreateTable en CREATE TABLE `Warning`.

4. Uploaden en starten

  1. Upload in Files de bestanden package.json, prisma7.config.ts, index.ts en de map prisma met schema.prisma en migrations. Upload node_modules en generated niet: de server bouwt beide.
  2. Stel op de pagina Bot, in het paneel Code, het Startbestand in op index.ts.
  3. Druk op Starten en open Console. De installatie voert je postinstall uit: je ziet Generated Prisma Client (7.10.1), dan All migrations have been successfully applied., dan [PlayNHost] Starting your bot en Logged in as ....

De console toont ook npm warn install-scripts 2 packages had install scripts blocked voor prisma en @prisma/engines. De npm-versie in ons Node.js 22-image blokkeert installatiescripts van afhankelijkheden tenzij ze zijn toegestaan. Je eigen postinstall draait nog steeds, en in onze test genereerde, migreerde en bevroeg Prisma zonder die scripts.

Waarom niet prisma migrate dev?

prisma migrate dev maakt eerst een tijdelijke shadow-database aan om je migraties te controleren. De databasegebruiker van je bot kan werken binnen zijn eigen database, maar kan geen nieuwe aanmaken, dus op onze servers stopt het met:

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 past alleen migratiebestanden toe en heeft geen shadow-database nodig, daarom gebruikt de postinstall die opdracht. Je kunt migrate dev ook niet vanaf je eigen computer op de database van de bot richten: de database accepteert alleen verbindingen van PlayNHost-servers en phpMyAdmin.

Het schema later wijzigen

  1. Voer op je computer npx prisma migrate dev --name uit plus een naam voor de wijziging, tegen een eigen MySQL of MariaDB, bijvoorbeeld een in Docker: zet DATABASE_URL in je terminal op die database, want prisma7.config.ts leest hem uit de omgeving. Het schrijft een nieuwe map in prisma/migrations.
  2. Upload de nieuwe migratiemap en het gewijzigde schema.prisma.
  3. De server voert npm install, en daarmee je postinstall, alleen uit als package.json, package-lock.json of de Node.js-versie is gewijzigd, als node_modules ontbreekt, of bij de eerste start meer dan een dag na de laatste installatie. Om de migratie nu toe te passen, wijzig je package.json (bijvoorbeeld de version) of verwijder je de map node_modules in Files, en start je opnieuw.

Geheugen: de installatie is het zware deel

We hebben dit project vanaf nul geïnstalleerd in een container die op 512 MB was begrensd, zoals ons kleinste botabonnement. De installatie, met prisma generate en prisma migrate deploy, duurde 41 seconden en piekte op 386 MB geheugen (bestandscache niet meegerekend) in één run en 417 MB in een tweede run, zonder out-of-memory-kill in beide. Het past in het abonnement van 512 MB met minder dan 100 MB over, dus een groter project of meer pakketten kan eroverheen gaan. Dezelfde installatie zonder Prisma (alleen discord.js en mysql2) piekte op 147 MB.

Limieten en fouten

  • 20 verbindingen tegelijk per databasegebruiker, over alle processen die dezelfde login gebruiken. Houd connectionLimit laag; 5 is ruim voldoende voor een bot.
  • Een query die langer dan 120 seconden duurt, wordt gestopt, met fout 1969 Query execution was interrupted (max_statement_time exceeded).
  • 500 MB per database: bij 500 MB worden schrijfacties gestopt totdat je rijen verwijdert. Serverbackups bevatten de database niet. Exporteer hem af en toe uit phpMyAdmin.
  • Verbindingsproblemen hebben dezelfde oorzaken als bij mysql2 (verkeerd wachtwoord, verkeerde host of poort, een ontbrekende variabele in Omgeving, verbinden vanaf je eigen computer), maar de meldingen zijn anders: de MariaDB-adapter gebruikt de mariadb-driver, niet mysql2, en we hebben de meldingen ervan niet vastgelegd. De oorzaken staan in Een MySQL-database gebruiken met een discord.js-bot.

Liever gewone SQL met mysql2 of aiomysql: discord.js (mysql2), discord.py (aiomysql). Het overzicht: Een database gebruiken met je Discord-bot. Een botserver met database inbegrepen nodig? Bekijk de botabonnementen.

Draait je bot al ergens anders? Verhuis hem naar ons

ONZE NIEUWSTE BLOGS