Qual banco de dados usar no seu bot do Discord

Para a maioria dos bots, SQLite ou PostgreSQL resolvem. SQLite basta para um processo só e poucos dados. Use PostgreSQL ou MongoDB quando houver shards, vários processos ou muita escrita.

Atualizado em 6 min de leitura

Objetivos de aprendizado

Depois de ler este artigo, você vai conseguir:

  • Escolher entre SQLite, PostgreSQL, MongoDB e Redis para o seu bot
  • Ver o código de conexão de cada opção em Node.js
  • Evitar os erros que fazem um bot perder dados
Nesta página

Quase todo bot acaba precisando guardar algo: o prefixo de cada servidor, o saldo de uma economia, as advertências de moderação, o canal de boas-vindas. O que fica numa variável some no próximo restart, então você precisa de um banco de dados.

A lista parece longa, mas na prática são quatro candidatos: SQLite, PostgreSQL, MongoDB e Redis. O que decide entre eles é o jeito como o seu bot grava e relaciona os dados.

Só cooldowns ou cache?simRedisou Map em memórianãoUm processo e poucos dados?simSQLiteum arquivonãoCampos variam por registro?simMongoDBsem esquema fixonãoPostgreSQLtabelas e JOIN
Cada pergunta corta uma opção: SQLite para um processo só, MongoDB ou PostgreSQL quando vários processos gravam.

Quais critérios importam na hora de escolher?#

Quatro perguntas resolvem quase todos os casos:

  1. Quantos processos gravam no banco? Um bot de um processo só é diferente de um bot com shards em processos separados, ou de um bot e um painel web que gravam juntos.
  2. Seus dados têm relações? Usuários com itens, itens com trocas, trocas com histórico: isso pede tabelas e JOIN. Configurações soltas por servidor não pedem.
  3. O que acontece com os dados num deploy? Se o banco é um arquivo dentro da pasta do bot, um deploy que substitui a pasta pode levar o arquivo junto. Um banco em servidor separado não tem esse risco.
  4. Quanta operação você aceita? Um arquivo não precisa de servidor. Um banco cliente-servidor precisa de host, credenciais e conexão.

Quando SQLite é a melhor escolha?#

SQLite é um banco em um único arquivo, sem servidor. Em Node.js, a biblioteca better-sqlite3 é síncrona e simples. Os exemplos deste artigo usam ES modules ("type": "module" no package.json) e uma versão recente do Node.js, como pede a versão atual do better-sqlite3:

js
import Database from 'better-sqlite3';

const db = new Database('bot.db');
db.pragma('journal_mode = WAL');

db.exec(`
  CREATE TABLE IF NOT EXISTS guild_settings (
    guild_id TEXT PRIMARY KEY,
    prefix TEXT NOT NULL DEFAULT '!'
  )
`);

const setPrefix = db.prepare(`
  INSERT INTO guild_settings (guild_id, prefix) VALUES (?, ?)
  ON CONFLICT(guild_id) DO UPDATE SET prefix = excluded.prefix
`);
const getPrefix = db.prepare('SELECT prefix FROM guild_settings WHERE guild_id = ?');

setPrefix.run('123456789012345678', '?');
console.log(getPrefix.get('123456789012345678')?.prefix); // "?"

Prós: zero configuração, rápido, ótimo para começar, não depende de rede.

Contras: a documentação do SQLite diz que ele aceita apenas um escritor por vez por arquivo de banco, e que não é indicado quando muitos computadores acessam o mesmo banco pela rede. Se o arquivo mora em um disco que some ou é substituído a cada deploy, você perde os dados.

Use quando: é um único processo, os dados cabem em um arquivo e você sabe onde esse arquivo fica guardado de forma durável.

Quando PostgreSQL é a melhor escolha?#

PostgreSQL é um banco relacional cliente-servidor. Ele foi feito para muitas conexões e escritas simultâneas (com controle de concorrência MVCC), tem transações e permite consultas com relações. É a escolha mais segura para economia, ranking, tickets e qualquer coisa em que perder ou duplicar um registro dói. Veja mais em o que é PostgreSQL.

Com a biblioteca pg, use um Pool e crie só um para o bot inteiro:

js
import pg from 'pg';

const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL });
// sem este listener, um erro em uma conexão ociosa derruba o processo
pool.on('error', (err) => console.error('erro no pool do PostgreSQL', err));

await pool.query(`
  CREATE TABLE IF NOT EXISTS balances (
    user_id TEXT PRIMARY KEY,
    coins BIGINT NOT NULL DEFAULT 0
  )
`);

// $1, $2: parâmetros evitam SQL injection
await pool.query(
  `INSERT INTO balances (user_id, coins) VALUES ($1, $2)
   ON CONFLICT (user_id) DO UPDATE SET coins = balances.coins + excluded.coins`,
  ['123456789012345678', 50],
);

const { rows } = await pool.query('SELECT coins FROM balances WHERE user_id = $1', ['123456789012345678']);
console.log(rows[0].coins);

Prós: transações, integridade, escala com vários processos, o dado fica fora da pasta do bot.

Contras: exige um servidor de banco e uma string de conexão. Para um bot de cinco comandos, é mais do que o necessário.

Quando MongoDB faz sentido?#

MongoDB guarda documentos parecidos com JSON, sem esquema fixo. É cômodo quando cada registro tem campos diferentes ou o formato muda a cada versão do bot, como um perfil de jogador com itens variados. Mais detalhes em o que é MongoDB e quando usar.

js
import { MongoClient } from 'mongodb';

const client = new MongoClient(process.env.MONGODB_URI);
await client.connect();

const profiles = client.db('bot').collection('profiles');

await profiles.updateOne(
  { _id: '123456789012345678' },
  { $inc: { xp: 10 }, $setOnInsert: { createdAt: new Date() } },
  { upsert: true },
);

console.log(await profiles.findOne({ _id: '123456789012345678' }));

Prós: modelagem flexível, upsert e $inc atômicos.

Contras: sem esquema, é fácil gravar campos inconsistentes, e as relações entre coleções ficam por sua conta. Se você usa o cluster gratuito do MongoDB Atlas, saiba que ele tem limite de armazenamento (índices contam) e pausa clusters que ficam muito tempo sem conexão. Confira os valores atuais na documentação de limites do Atlas.

Redis serve como banco do bot?#

Redis guarda os dados em memória (a gravação em disco é opcional e configurável) e é muito rápido. Ele é ótimo para cooldowns, contadores, filas e cache, mas dados que não podem se perder devem ficar em um banco principal. Entenda a diferença em Redis como cache ou como banco. Antes de adicionar Redis, pergunte se uma Map em memória já resolve: para um processo só, geralmente resolve.

Qual banco escolher em cada cenário?#

Cenário Recomendação
Bot pequeno, um processo, configurações por servidor SQLite
Economia, ranking, tickets, transações PostgreSQL
Perfis com campos variáveis, protótipo rápido MongoDB
Bot com shards em processos separados PostgreSQL ou MongoDB
Bot e painel web gravando nos mesmos dados PostgreSQL ou MongoDB
Cooldowns e contadores temporários Map em memória ou Redis
Dados de teste JSON ou SQLite

Na dúvida, comece com SQLite se o bot é pequeno e você sabe onde o arquivo fica guardado, ou com PostgreSQL se já sabe que o bot vai crescer. Trocar depois é possível, mas dá trabalho de migração.

Quais erros fazem um bot perder dados?#

  • Banco dentro da pasta que o deploy substitui. Confirme que o arquivo sobrevive a um novo envio, ou use um banco externo.
  • Uma conexão nova a cada comando. Crie o cliente ou o Pool uma vez, ao iniciar, e reutilize.
  • Concatenar texto em SQL. Use sempre parâmetros (?, $1).
  • Credenciais no código. Coloque a string de conexão em variável de ambiente, como no caso do token do bot.
  • Nenhuma cópia dos dados. Teste restaurar uma cópia antes de precisar dela.

Quanto o banco pesa na conta mensal do bot aparece em quanto custa hospedar um bot.

Como fazer isso na Square Cloud#

Depois de escolher o banco, falta decidir onde ele roda. Na Square Cloud, o Hobby (a partir de R$ 12,49 por mês) hospeda o bot, mas não inclui hospedagem de banco. A hospedagem de MongoDB, PostgreSQL, MySQL e Redis começa no plano Standard.

Você cria o banco no painel (nome, memória e motor), no mesmo datacenter das aplicações, com latência média de cerca de 1 ms. O painel entrega a URL de conexão completa e o certificado para baixar. O TLS é obrigatório, então o cliente precisa confiar na CA baixada (no PostgreSQL e no MongoDB ele também apresenta o certificado .pem). Coloque a URL na variável de ambiente do bot (DATABASE_URL ou MONGODB_URI, como nos exemplos acima).

Cada banco é um nó único, com até 512 conexões. O mínimo é de 512 MB para Redis e 1024 MB para os outros motores. Você tira snapshots manuais quando quiser, e todo plano com bancos inclui também um snapshot automático diário. Um snapshot restaura no mesmo banco ou num banco novo.

Se o seu bot é pequeno, SQLite no Hobby continua sendo uma opção válida: o disco da aplicação persiste entre restarts e deploys, e um novo commit sobrescreve só os arquivos que estão no ZIP, então deixe o arquivo do banco fora dele. Veja a hospedagem de bots do Discord e os planos.

Hospedagem de bots

Seu bot online 24/7, com reinício automático

Saiba mais

Perguntas frequentes

Posso guardar os dados do bot em um arquivo JSON?

Para testes, sim. Em produção não: duas escritas ao mesmo tempo ou uma queda no meio da gravação podem corromper o arquivo, e você reescreve tudo a cada alteração. SQLite resolve isso com a mesma simplicidade.

SQLite aguenta um bot grande?

Aguenta bastante leitura, mas só permite um escritor por vez por arquivo. Para um único processo isso raramente é problema. Com shards em processos separados ou vários processos gravando, prefira um banco cliente-servidor.

MongoDB ou PostgreSQL para bot do Discord?

Os dois funcionam. PostgreSQL é melhor quando os dados têm relações (usuários, itens, transações). MongoDB é cômodo quando cada documento tem formato diferente ou muda com frequência.

Preciso de Redis no meu bot?

Só se você precisa de cache ou contadores muito rápidos, como cooldowns e filas. Redis não substitui o banco principal. Para a maioria dos bots, uma Map em memória ou o próprio banco bastam.

Próximo artigoBot online mas não responde aos comandos: causas comunsBot do Discord online que ignora comandos costuma ter intent de conteúdo desligada, comando não registrado, falta de permissão ou resposta lenta demais.