NOVO 📢 Oferta de lançamento limitada: até 70% de desconto no seu primeiro mês, em todos os jogos!Ver planos

Como usar o Prisma com MySQL em um bot do Discord

Hospedagem de bot do DiscordUpdated Nov 6, 202610 min read

Resposta curta. Use o Prisma 7 com o adaptador de driver @prisma/adapter-mariadb, que o Prisma documenta para MySQL e MariaDB. Na PlayNHost, o prisma migrate dev não consegue rodar no banco de dados do seu bot, porque precisa criar um banco "shadow" temporário e os usuários de banco de dados aqui não podem criar bancos. Escreva a primeira migração com o prisma migrate diff, que não precisa de banco nenhum, e deixe um script postinstall rodar prisma generate e prisma migrate deploy no servidor. Todo plano de bot inclui um banco de dados MySQL. Testamos o código e os comandos abaixo em 6 de novembro de 2026 com Prisma 7.10.1, discord.js 14 e Node.js 22, contra MariaDB 10.11 com uma conta de banco configurada como a nossa (sem permissão para criar bancos, 20 conexões, limite de consulta de 120 segundos), mas não com um login real no Discord.

Prisma 7, não a versão mais recente do npm

Em 6 de novembro de 2026, a tag latest do pacote prisma no npm aponta para um release candidate do Prisma 8 (8.0.0-rc.30), e o quickstart de MySQL do próprio Prisma é o do Prisma 7: ele instala prisma@prev e @prisma/client@7. Por isso este guia fixa todos os pacotes do Prisma em ^7. Um simples npm install prisma daria a você a versão 8.

O que você precisa

  • Um bot discord.js escrito em TypeScript. Nosso tipo Node.js Bot executa com tsx um arquivo principal terminado em .ts, então nada precisa ser compilado antes: Como rodar um bot do Discord em TypeScript.
  • Um plano de bot da PlayNHost com o tipo Node.js Bot. Todo plano inclui um banco de dados.
  • Node.js e npm no seu próprio computador, para escrever a migração uma vez.

1. Crie o banco de dados e as variáveis

  1. Abra o servidor do seu bot no painel, clique em Bancos de dados, depois em Novo banco de dados e em Criar banco de dados.
  2. A página do novo banco de dados abre (mais tarde, clique nele em Seus bancos de dados). Deixe-a aberta e, em Config pronta para usar, escolha Generic URL em Config para: é a string de conexão mysql://... de que a linha de comando do Prisma precisa.
  3. Abra a página Bot, o painel Ambiente, clique em Colar um arquivo .env e cole seis linhas com seus valores. Para DATABASE_URL, cole a Generic URL exatamente como é mostrada: as senhas dos nossos bancos podem conter caracteres como @ e +, que o Prisma precisa codificados com porcentagem dentro de uma URL, e a Generic URL já vem codificada.
    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 e DATABASE_URL recebem um selo Segredo: eles são salvos como somente escrita. Clique em Importar 6 variáveis.

O bot em si se conecta com os cinco valores DB_, passados ao adaptador um por um como no quickstart do Prisma. O DATABASE_URL serve apenas para os comandos prisma que rodam durante a instalação.

2. Os arquivos do projeto

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 (o Prisma 7.10 e posteriores usam este nome, para não colidir com o prisma.config.ts do 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) e a verificação !interaction.guildId mantêm o /warn fora das mensagens diretas. O try/catch importa: sem ele, o discord.js entrega uma consulta que falhou ao seu evento error, e quando ninguém escuta ali, o Node.js derruba o bot.

3. Escreva a primeira migração no seu computador

Na pasta do projeto, no seu próprio computador (no Windows, crie as pastas prisma\migrations\0_init manualmente em vez de usar 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 compara um banco vazio com o seu schema e escreve o SQL, sem se conectar a nenhum banco de dados. --ignore-scripts pula o postinstall, que tentaria alcançar um banco. O arquivo começa com -- CreateTable e CREATE TABLE `Warning`.

4. Envie os arquivos e inicie

  1. Em Files, envie package.json, prisma7.config.ts, index.ts e a pasta prisma com schema.prisma e migrations. Não envie node_modules nem generated: o servidor cria os dois.
  2. Na página Bot, painel Código, defina Arquivo de entrada como index.ts.
  3. Pressione Iniciar e abra o Console. A instalação roda o seu postinstall: você vê Generated Prisma Client (7.10.1), depois All migrations have been successfully applied., depois [PlayNHost] Starting your bot e Logged in as ....

O console também mostra npm warn install-scripts 2 packages had install scripts blocked para prisma e @prisma/engines. A versão do npm na nossa imagem de Node.js 22 bloqueia os scripts de instalação das dependências, a menos que sejam permitidos. O seu próprio postinstall continua rodando, e no nosso teste o Prisma gerou, migrou e consultou sem esses scripts.

Por que não o prisma migrate dev?

O prisma migrate dev primeiro cria um banco shadow temporário para verificar as suas migrações. O usuário de banco do seu bot pode trabalhar dentro do próprio banco, mas não pode criar novos, então nos nossos servidores ele para com:

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_...`

O prisma migrate deploy apenas aplica arquivos de migração e não precisa de banco shadow, e é por isso que o postinstall o usa. Você também não consegue apontar o migrate dev para o banco do bot a partir do seu computador: o banco só aceita conexões de servidores da PlayNHost e do phpMyAdmin.

Mudando o schema depois

  1. No seu computador, rode npx prisma migrate dev --name mais um nome para a mudança, contra um MySQL ou MariaDB seu, como um em Docker: defina DATABASE_URL no seu terminal para esse banco, porque o prisma7.config.ts a lê do ambiente. Ele escreve uma pasta nova em prisma/migrations.
  2. Envie a nova pasta de migração e o schema.prisma alterado.
  3. O servidor roda npm install, e com ele o seu postinstall, somente quando package.json, package-lock.json ou a versão do Node.js mudaram, quando node_modules está ausente, ou na primeira inicialização mais de um dia depois da última instalação. Para aplicar a migração agora, altere o package.json (por exemplo, o campo version) ou apague a pasta node_modules em Files, e reinicie.

Memória: a instalação é a parte pesada

Instalamos este projeto do zero em um contêiner limitado a 512 MB, como o nosso menor plano de bot. A instalação, com prisma generate e prisma migrate deploy, levou 41 segundos e chegou a um pico de 386 MB de memória (sem contar o cache de arquivos) em uma execução e 417 MB em uma segunda, sem nenhum encerramento por falta de memória em nenhuma delas. Ela cabe no plano de 512 MB com menos de 100 MB de folga, então um projeto maior ou mais pacotes podem estourar. A mesma instalação sem o Prisma (apenas discord.js e mysql2) chegou a um pico de 147 MB.

Limites e erros

  • 20 conexões ao mesmo tempo por usuário de banco, somando todos os processos que usam o mesmo login. Mantenha o connectionLimit baixo; 5 é mais que suficiente para um bot.
  • Uma consulta que roda por mais de 120 segundos é interrompida, com o erro 1969 Query execution was interrupted (max_statement_time exceeded).
  • 500 MB por banco de dados: ao chegar a 500 MB, as gravações são paradas até você apagar linhas. Os backups do servidor não incluem o banco de dados. Exporte-o do phpMyAdmin de vez em quando.
  • Os problemas de conexão têm as mesmas causas que com o mysql2 (senha errada, host ou porta errados, uma variável faltando em Ambiente, conexão a partir do seu próprio computador), mas as mensagens são diferentes: o adaptador do MariaDB usa o driver mariadb, não o mysql2, e não registramos as mensagens dele. As causas estão listadas em Como usar um banco de dados MySQL com um bot discord.js.

Prefere SQL puro com mysql2 ou aiomysql? discord.js (mysql2), discord.py (aiomysql). A visão geral: Como usar um banco de dados com seu bot do Discord. Precisa de um servidor de bot com banco de dados incluso? Veja os planos de bot.

Seu bot já está em outro lugar? Traga-o para nós

NOSSOS ÚLTIMOS ARTIGOS