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

Como usar um banco de dados MySQL com um bot discord.js (mysql2)

Hospedagem de bot do DiscordUpdated Nov 6, 20268 min read

Resposta curta. Adicione mysql2 ao seu bot discord.js, crie um único pool de conexões a partir de variáveis de ambiente quando o bot iniciar e execute toda consulta por ele, com marcadores ?. Na PlayNHost, todo plano de bot inclui um banco de dados MySQL: crie-o na página Bancos de dados, cole os dados dele no painel Ambiente e o código abaixo funciona como está. O banco aceita conexões dos seus servidores PlayNHost e do phpMyAdmin, não do seu próprio computador. Testamos este código em 6 de novembro de 2026 com discord.js 14.28.1, mysql2 3.24.6 e Node.js 22, contra o MariaDB 10.11 com uma conta de banco configurada como a nossa (20 conexões, limite de consulta de 120 segundos): a inicialização, a tabela e o handler de /warn, mas não um login real no Discord.

O que você precisa

1. Crie o banco de dados

  1. Abra seu servidor de bot no painel e clique em Bancos de dados.
  2. Clique em Novo banco de dados. O Rótulo é opcional; Será criado como mostra o nome completo. Clique em Criar banco de dados.
  3. A página do novo banco de dados abre (depois, clique nele em Seus bancos de dados). O cartão Conexão mostra Host, Porta, Banco de dados, Nome de usuário e Senha. Em Config pronta para usar, Config para tem um trecho Node.js (mysql2) com os mesmos valores já preenchidos.

2. Coloque os dados em Ambiente

  1. Abra a página Bot, o painel Ambiente, e clique em Colar um arquivo .env.
  2. Cole estas cinco linhas com os valores do cartão Conexão:
    DB_HOST=your-host
    DB_PORT=3306
    DB_NAME=your-database
    DB_USER=your-username
    DB_PASSWORD=your-password
  3. DB_PASSWORD recebe um selo Segredo: ele é salvo somente para escrita e nunca mais é exibido. Clique em Importar 5 variáveis.

Use a porta real do cartão Conexão. Manter a senha fora do código significa que ela nunca vai parar em um arquivo ou em um repositório Git. Mais sobre este painel: Como definir variáveis de ambiente e segredos para o seu bot do Discord.

3. O código

package.json

{
  "name": "my-discord-bot",
  "private": true,
  "main": "index.js",
  "dependencies": {
    "discord.js": "^14",
    "mysql2": "^3"
  }
}

index.js

const { Client, Events, GatewayIntentBits, InteractionContextType, MessageFlags, REST, Routes, SlashCommandBuilder } = require('discord.js');
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),
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  database: process.env.DB_NAME,
  connectionLimit: 5,
});

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 () => {
  await new REST().setToken(process.env.DISCORD_TOKEN).put(Routes.applicationCommands(client.user.id), { body: commands });
  console.log(`Logged in as ${client.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 pool.execute('INSERT INTO warnings (guild_id, user_id, reason) VALUES (?, ?, ?)', [interaction.guildId, user.id, reason]);
    const [rows] = await pool.execute('SELECT COUNT(*) AS total FROM warnings WHERE guild_id = ? AND user_id = ?', [interaction.guildId, user.id]);
    await interaction.reply(`${user} has ${rows[0].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);
  }
});

async function main() {
  await pool.query(`CREATE TABLE IF NOT EXISTS warnings (
    id INT AUTO_INCREMENT PRIMARY KEY,
    guild_id VARCHAR(20) NOT NULL,
    user_id VARCHAR(20) NOT NULL,
    reason VARCHAR(200) NOT NULL,
    created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    KEY guild_user (guild_id, user_id)
  )`);
  console.log('Database ready');
  await client.login(process.env.DISCORD_TOKEN);
}

main().catch((err) => {
  console.error(err);
  process.exit(1);
});

O que ele faz: quando o bot inicia, cria a tabela warnings se ela não existir, imprime Database ready e depois faz o login. /warn salva um aviso e responde com o total do membro. O discord.js entrega os IDs do Discord como strings, porque são grandes demais para um número do JavaScript, então eles são guardados como texto. Os marcadores ? enviam o texto do membro ao banco separadamente do SQL, então um motivo como '); DROP TABLE warnings; -- é salvo como texto simples.

Duas linhas mantêm o bot de pé. setContexts(InteractionContextType.Guild) e a verificação !interaction.guildId impedem /warn em mensagem direta, onde não há servidor para associar o aviso. O try/catch responde com uma mensagem privada curta quando uma consulta falha. Sem ele, o discord.js passa o erro para o evento error e, quando ninguém escuta ali, o Node.js derruba o bot. No nosso teste, apagamos a tabela e executamos /warn: o bot registrou ER_NO_SUCH_TABLE, respondeu Could not save the warning. Try again later. e continuou rodando.

4. Execute

  1. Envie index.js e package.json em Files. Não envie node_modules.
  2. Clique em Iniciar e abra Console. A inicialização instala os pacotes e depois mostra [PlayNHost] Starting your bot, Database ready e Logged in as ....
  3. No Discord, digite /warn. Para ver as linhas, abra a página do banco de dados e clique em Abrir no phpMyAdmin.

Limites que importam para o seu pool

  • 20 conexões ao mesmo tempo por usuário do banco. Todo processo que usa o mesmo login conta: seu bot, uma segunda cópia dele, um painel. A conexão 21 é recusada com ER_USER_LIMIT_REACHED. No nosso teste, um pool com connectionLimit: 30 falhou com esse erro assim que 30 consultas rodaram ao mesmo tempo. Um pool de 5 nunca abre mais de 5: as consultas extras esperam por uma conexão livre.
  • Uma consulta que roda por mais de 120 segundos é interrompida. O MariaDB a encerra com o erro 1969, Query execution was interrupted (max_statement_time exceeded). No nosso teste isso aconteceu após 120.03 segundos. As consultas de um bot normalmente levam milissegundos, então isso só atinge uma consulta descontrolada.
  • 500 MB por banco de dados. A página Bancos de dados mostra quanto está em uso. Ao chegar a 500 MB, as gravações são bloqueadas até você apagar linhas.
  • Os backups do servidor não incluem o banco de dados. Exporte-o do phpMyAdmin de vez em quando.

Erros e o que significam

Estas são as mensagens exatas que o mysql2 deu no nosso teste:

  • ER_ACCESS_DENIED_ERROR ... Access denied for user: DB_USER ou DB_PASSWORD incorreto. Depois de Redefinir senha no cartão Conexão, a senha antiga para de funcionar na hora: troque DB_PASSWORD e reinicie o bot.
  • ECONNREFUSED: nada aceitou a conexão nesse host e porta. Confira DB_HOST e DB_PORT. Um DB_HOST ausente também termina aqui, porque o mysql2 então se conecta a localhost, onde nenhum banco roda: a variável está faltando em Ambiente, ou o bot não foi reiniciado depois de adicioná-la.
  • ENOTFOUND: DB_HOST está escrito errado, então o nome não existe.
  • ETIMEDOUT: nada respondeu em 10 segundos, a espera padrão do mysql2. Qualquer que seja a mensagem, uma conexão a partir do seu próprio computador falha: o banco só aceita conexões de servidores PlayNHost e do phpMyAdmin.
  • ER_USER_LIMIT_REACHED ... has exceeded the 'max_user_connections' resource (current value: 20): conexões abertas demais, veja os limites acima. Geralmente é uma conexão nova por comando que nunca é fechada, ou uma segunda cópia do bot.

Mais erros de bots: Erros comuns de bot do Discord e o que significam.

Quanta memória ele acrescenta

Instalar discord.js e mysql2 do zero chegou a um pico de 147 MB de memória (cache de arquivos não contado) no nosso teste, em um contêiner limitado a 512 MB como o nosso menor plano de bot. Do início até o Discord recusar o nosso token de teste, o bot usou no máximo 31 MB; ele tinha criado a tabela, mas nunca fez login. Um bot logado e em muitos servidores usa mais: De quanta RAM um bot do Discord precisa?

O mesmo em Python, ou com Prisma

Precisa de um servidor de bot com banco de dados incluso? Veja os planos para bots.

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

NOSSOS ÚLTIMOS ARTIGOS