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

Prisma mit MySQL in einem Discord-Bot nutzen

Discord-Bot-HostingUpdated Nov 6, 20269 min read

Kurze Antwort. Nutze Prisma 7 mit dem Driver-Adapter @prisma/adapter-mariadb, den Prisma für MySQL und MariaDB dokumentiert. Bei PlayNHost kann prisma migrate dev nicht gegen die Datenbank deines Bots laufen, weil dafür eine temporäre „Shadow"-Datenbank angelegt werden muss und Datenbank-Benutzer hier keine Datenbanken erstellen dürfen. Schreib die erste Migration stattdessen mit prisma migrate diff, das keine Datenbank braucht, und lass ein postinstall-Skript auf dem Server prisma generate und prisma migrate deploy ausführen. Jeder Bot-Tarif enthält eine MySQL-Datenbank. Wir haben den Code und die Befehle unten am 6. November 2026 mit Prisma 7.10.1, discord.js 14 und Node.js 22 getestet, gegen MariaDB 10.11 mit einem Datenbank-Konto wie bei uns (kein Recht, Datenbanken zu erstellen, 20 Verbindungen, 120 Sekunden Abfragelimit), aber nicht mit einem echten Discord-Login.

Prisma 7, nicht die neueste npm-Version

Am 6. November 2026 zeigt der Tag latest des Pakets prisma auf npm auf einen Release Candidate von Prisma 8 (8.0.0-rc.30), und die MySQL-Schnellstartanleitung von Prisma selbst ist die für Prisma 7: Sie installiert prisma@prev und @prisma/client@7. Deshalb setzt diese Anleitung alle Prisma-Pakete auf ^7. Ein einfaches npm install prisma würde dir Version 8 geben.

Was du brauchst

  • Einen discord.js-Bot, der in TypeScript geschrieben ist. Unser Bot-Typ Node.js Bot führt eine Hauptdatei auf .ts mit tsx aus, sodass vorher nichts kompiliert werden muss: So betreibst du einen TypeScript-Discord-Bot.
  • Einen PlayNHost-Bot-Tarif mit dem Typ Node.js Bot. Jeder Tarif enthält eine Datenbank.
  • Node.js und npm auf deinem eigenen Computer, um die Migration einmal zu schreiben.

1. Datenbank und Variablen anlegen

  1. Öffne deinen Bot-Server im Panel, klicke auf Datenbanken, dann auf Neue Datenbank und Datenbank erstellen.
  2. Die Seite der neuen Datenbank öffnet sich (später klickst du sie unter Deine Datenbanken an). Lass sie offen und wähle unter Fertige Config bei Config für den Eintrag Generic URL: Das ist der mysql://...-Connection-String, den die Prisma-Kommandozeile braucht.
  3. Öffne die Seite Bot, den Bereich Umgebung, klicke auf Eine .env-Datei einfügen und füge sechs Zeilen mit deinen Werten ein. Füge für DATABASE_URL die Generic URL genau so ein, wie sie angezeigt wird: Unsere Datenbank-Passwörter können Zeichen wie @ und + enthalten, die Prisma in einer URL prozentcodiert braucht, und die Generic URL ist bereits codiert.
    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 und DATABASE_URL bekommen ein Secret-Badge: Sie werden nur schreibbar gespeichert. Klicke auf 6 Variablen importieren.

Der Bot selbst verbindet sich mit den fünf DB_-Werten, die dem Adapter wie in Prismas Schnellstart einzeln übergeben werden. DATABASE_URL brauchen nur die prisma-Befehle, die bei der Installation laufen.

2. Die Projektdateien

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 und neuer nutzen diesen Namen, damit er nicht mit prisma.config.ts von Prisma 8 kollidiert)

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) und die Prüfung !interaction.guildId halten /warn aus Direktnachrichten heraus. Das try/catch ist wichtig: Ohne es gibt discord.js eine fehlgeschlagene Abfrage an sein error-Event weiter, und wenn dort niemand zuhört, beendet Node.js den Bot.

3. Die erste Migration auf deinem Computer schreiben

Führe im Projektordner auf deinem eigenen Computer Folgendes aus (unter Windows legst du die Ordner prisma\migrations\0_init von Hand an statt mit 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 vergleicht eine leere Datenbank mit deinem Schema und schreibt das SQL, ohne sich mit einer Datenbank zu verbinden. --ignore-scripts überspringt das postinstall, das versuchen würde, eine Datenbank zu erreichen. Die Datei beginnt mit -- CreateTable und CREATE TABLE `Warning`.

4. Hochladen und starten

  1. Lade unter Dateien package.json, prisma7.config.ts, index.ts und den Ordner prisma mit schema.prisma und migrations hoch. Lade node_modules und generated nicht hoch: Der Server baut beides.
  2. Stelle auf der Seite Bot im Bereich Code die Startdatei auf index.ts.
  3. Klicke auf Starten und öffne die Konsole. Die Installation führt dein postinstall aus: Du siehst Generated Prisma Client (7.10.1), dann All migrations have been successfully applied., dann [PlayNHost] Starting your bot und Logged in as ....

In der Konsole steht außerdem npm warn install-scripts 2 packages had install scripts blocked für prisma und @prisma/engines. Die npm-Version in unserem Node.js-22-Image blockiert Install-Skripte von Abhängigkeiten, solange sie nicht erlaubt sind. Dein eigenes postinstall läuft trotzdem, und in unserem Test hat Prisma auch ohne diese Skripte generiert, migriert und abgefragt.

Warum nicht prisma migrate dev?

prisma migrate dev legt zuerst eine temporäre Shadow-Datenbank an, um deine Migrationen zu prüfen. Der Datenbank-Benutzer deines Bots kann in seiner eigenen Datenbank arbeiten, aber keine neuen anlegen, deshalb bricht der Befehl auf unseren Servern so ab:

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 wendet nur Migrationsdateien an und braucht keine Shadow-Datenbank, deshalb nutzt das postinstall diesen Befehl. Du kannst migrate dev auch nicht von deinem eigenen Computer aus auf die Datenbank des Bots richten: Die Datenbank nimmt nur Verbindungen von PlayNHost-Servern und von phpMyAdmin an.

Das Schema später ändern

  1. Führe auf deinem Computer npx prisma migrate dev --name plus einen Namen für die Änderung aus, gegen eine eigene MySQL- oder MariaDB-Datenbank, zum Beispiel in Docker: Setze DATABASE_URL in deinem Terminal auf diese Datenbank, denn prisma7.config.ts liest sie aus der Umgebung. Der Befehl schreibt einen neuen Ordner in prisma/migrations.
  2. Lade den neuen Migrationsordner und die geänderte schema.prisma hoch.
  3. Der Server führt npm install und damit dein postinstall nur aus, wenn sich package.json, package-lock.json oder die Node.js-Version geändert haben, wenn node_modules fehlt oder beim ersten Start mehr als einen Tag nach der letzten Installation. Um die Migration sofort anzuwenden, ändere package.json (zum Beispiel deren version) oder lösche den Ordner node_modules unter Dateien und starte dann neu.

Arbeitsspeicher: Die Installation ist der schwere Teil

Wir haben dieses Projekt von Grund auf in einem Container installiert, der auf 512 MB begrenzt war, wie unser kleinster Bot-Tarif. Die Installation mit prisma generate und prisma migrate deploy dauerte 41 Sekunden und erreichte in einem Lauf 386 MB Arbeitsspeicher (ohne Dateicache) und in einem zweiten Lauf 417 MB, ohne dass in einem der beiden Läufe ein Out-of-Memory-Kill auftrat. Sie passt in den 512-MB-Tarif mit weniger als 100 MB Reserve, ein größeres Projekt oder mehr Pakete könnten also darüber hinausgehen. Dieselbe Installation ohne Prisma (nur discord.js und mysql2) erreichte 147 MB.

Limits und Fehler

  • 20 gleichzeitige Verbindungen pro Datenbank-Benutzer, über alle Prozesse hinweg, die dasselbe Login nutzen. Halte connectionLimit niedrig; 5 reichen für einen Bot völlig.
  • Eine Abfrage, die länger als 120 Sekunden läuft, wird abgebrochen, mit dem Fehler 1969 Query execution was interrupted (max_statement_time exceeded).
  • 500 MB pro Datenbank: Bei 500 MB werden Schreibzugriffe gestoppt, bis du Zeilen löschst. Server-Backups enthalten die Datenbank nicht. Exportiere sie von Zeit zu Zeit aus phpMyAdmin.
  • Verbindungsprobleme haben dieselben Ursachen wie bei mysql2 (falsches Passwort, falscher Host oder Port, eine fehlende Variable unter Umgebung, Verbindung von deinem eigenen Computer), aber die Meldungen sehen anders aus: Der MariaDB-Adapter nutzt den mariadb-Treiber, nicht mysql2, und wir haben seine Meldungen nicht aufgezeichnet. Die Ursachen stehen in So nutzt du eine MySQL-Datenbank mit einem discord.js-Bot.

Lieber reines SQL mit mysql2 oder aiomysql: discord.js (mysql2), discord.py (aiomysql). Die Übersicht: So nutzt du eine Datenbank mit deinem Discord-Bot. Du brauchst einen Bot-Server mit Datenbank? Zu den Bot-Tarifen.

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

UNSERE NEUESTEN BLOGS