NapNodes

← Guías de Bots de Discord

Bots de Discord · actualizada el 25 de octubre de 2026

Base de datos para tu bot de Discord: guardar puntos, niveles y ajustes con SQLite

Tu bot cuenta puntos, guarda el canal de bienvenida o los avisos de cada usuario y, al reiniciarlo, todo vuelve a cero. Es porque lo guarda en memoria. Aquí va cómo guardarlo en un fichero que sobrevive a los reinicios, con SQLite, en Python y en JavaScript.

¿Se lo pasas a tu grupo?WhatsApp

Por qué tu bot lo olvida todo al reiniciarse

Lo que guardas en una variable (un diccionario de puntos en Python, un objeto o un Map en JavaScript) vive en la memoria del programa. Cuando el bot se reinicia, porque lo apagas, porque se actualiza o porque se cae, el programa empieza de cero y esa memoria desaparece.

Para que algo dure hay que escribirlo en el disco: en un fichero que el bot lee al arrancar. Lo más sencillo que funciona bien es SQLite, una base de datos que es un solo fichero, sin instalar ni mantener ningún servidor aparte.

¿Prefieres no montarlo tú? Hosting de bots de Discord desde 1,50 € al mes, sin permanencia y con devolución íntegra en 72 horas. O crea un bot de Discord gratis, sin tarjeta.

JSON, SQLite o una base de datos externa

Un fichero JSON vale para unos pocos ajustes que casi nunca cambian (el prefijo, el canal de registros). Para datos que cambian a cada rato, como puntos o niveles, se queda corto: hay que reescribir el fichero entero en cada cambio, dos comandos a la vez pueden pisarse y, si el bot se corta justo mientras escribe, el fichero puede quedar a medias y no volver a abrirse.

SQLite guarda cada cambio de golpe o no lo guarda (transacciones), busca rápido aunque tengas miles de usuarios y viene con Python (el módulo sqlite3) y con Node.js 22.13 en adelante y 24 (el módulo node:sqlite). Para un bot que está en uno o en cientos de servidores de Discord, es lo que mejor encaja.

Una base de datos externa (MongoDB, PostgreSQL, MySQL en otro servicio) tiene sentido cuando varios programas tienen que leer y escribir lo mismo a la vez, por ejemplo tu bot y una web con el ranking. A cambio, es otra cuenta, otra contraseña que guardar y cada consulta viaja por internet.

En Python, con sqlite3

No hay que instalar nada: sqlite3 viene con Python. Este bot tiene dos comandos, /punto, que te suma un punto, y /puntos, que te dice cuántos llevas. Apágalo y vuelve a encenderlo: los puntos siguen ahí. El requirements.txt y el .env son los de tu primer bot en Python.

sqlite3 es síncrono: mientras hace una consulta, el bot espera. Con consultas pequeñas como estas no se nota; si un día haces consultas pesadas, la librería aiosqlite hace lo mismo sin pararlo.

app.py
import os
import sqlite3
from pathlib import Path

import discord
from discord import app_commands
from dotenv import load_dotenv

load_dotenv()

# El fichero, junto a app.py: así da igual desde qué carpeta se arranque.
RUTA = Path(__file__).with_name("datos.db")
db = sqlite3.connect(RUTA)
db.execute("CREATE TABLE IF NOT EXISTS puntos (usuario INTEGER PRIMARY KEY, total INTEGER NOT NULL DEFAULT 0)")
db.commit()

client = discord.Client(intents=discord.Intents.default())
tree = app_commands.CommandTree(client)


@tree.command(name="punto", description="Te suma un punto")
async def punto(interaction: discord.Interaction):
    with db:  # guarda al salir del bloque; si algo falla, no guarda nada a medias
        db.execute(
            "INSERT INTO puntos (usuario, total) VALUES (?, 1) "
            "ON CONFLICT(usuario) DO UPDATE SET total = total + 1",
            (interaction.user.id,),
        )
    await interaction.response.send_message("➕ Un punto más")


@tree.command(name="puntos", description="Cuántos puntos tienes")
async def puntos(interaction: discord.Interaction):
    fila = db.execute("SELECT total FROM puntos WHERE usuario = ?", (interaction.user.id,)).fetchone()
    await interaction.response.send_message(f"Tienes {fila[0] if fila else 0} puntos")


@client.event
async def on_ready():
    await tree.sync()
    print(f"Conectado como {client.user}")


client.run(os.environ["DISCORD_TOKEN"])

En JavaScript, con node:sqlite

Desde Node.js 22.13, y en Node.js 24, SQLite viene incluido como node:sqlite, sin instalar nada. Al arrancar, la consola dice que es una función experimental (ExperimentalWarning): es normal y el bot funciona igual. El package.json y el .env son los de tu primer bot en JavaScript.

Guarda el ID de cada usuario como texto: los IDs de Discord son más grandes de lo que JavaScript puede guardar como número sin redondear, y discord.js ya te los da como texto.

¿Tienes que seguir en Node.js 20? Ahí no está node:sqlite: añade la librería better-sqlite3 a package.json; su forma de usarse (prepare, run, get) es casi igual que la de este ejemplo.

index.js
require("dotenv").config();
const path = require("node:path");
const { DatabaseSync } = require("node:sqlite"); // viene con Node.js 22.13+ y 24
const { Client, Events, GatewayIntentBits, SlashCommandBuilder } = require("discord.js");

// El fichero, junto a index.js: así da igual desde qué carpeta se arranque.
const db = new DatabaseSync(path.join(__dirname, "datos.db"));
db.exec("CREATE TABLE IF NOT EXISTS puntos (usuario TEXT PRIMARY KEY, total INTEGER NOT NULL DEFAULT 0)");
const sumar = db.prepare(
  "INSERT INTO puntos (usuario, total) VALUES (?, 1) ON CONFLICT(usuario) DO UPDATE SET total = total + 1",
);
const leer = db.prepare("SELECT total FROM puntos WHERE usuario = ?");

const client = new Client({ intents: [GatewayIntentBits.Guilds] });
const comandos = [
  new SlashCommandBuilder().setName("punto").setDescription("Te suma un punto"),
  new SlashCommandBuilder().setName("puntos").setDescription("Cuántos puntos tienes"),
];

client.once(Events.ClientReady, async (c) => {
  await c.application.commands.set(comandos.map((x) => x.toJSON()));
  console.log(`Conectado como ${c.user.tag}`);
});

client.on(Events.InteractionCreate, async (i) => {
  if (!i.isChatInputCommand()) return;
  if (i.commandName === "punto") {
    sumar.run(i.user.id); // el ID como texto: los de Discord no caben en un número de JavaScript
    await i.reply("➕ Un punto más");
  }
  if (i.commandName === "puntos") {
    const fila = leer.get(i.user.id);
    await i.reply(`Tienes ${fila ? fila.total : 0} puntos`);
  }
});

client.login(process.env.DISCORD_TOKEN);

Dónde va el fichero, y qué no subir a GitHub

Pon la base de datos junto al código, con la ruta sacada de la carpeta del propio programa (Path(__file__) en Python, __dirname en JavaScript), como en los ejemplos. Con una ruta suelta como «datos.db», el fichero se crea en la carpeta desde la que se arranca el bot, y si un día cambia, el bot empieza con una base de datos nueva y vacía y parece que lo ha perdido todo.

Si tu código está en GitHub, añade datos.db a .gitignore (y también el .env). La base de datos no es código: tiene los IDs de tus usuarios y cambia sola cada vez que alguien usa un comando. Si está en el repositorio, cada actualización del código puede chocar con la que tiene el bot o pisarla.

.gitignore
.env
datos.db
datos.db-*

Copias de la base de datos

Copiar el fichero con el bot encendido puede darte una copia a medias si justo estaba escribiendo. O apaga el bot antes de copiarlo, o haz la copia desde el propio programa: en Python, con backup(), que copia bien aunque el bot esté escribiendo; en SQL, con «VACUUM INTO 'copia.db'», que vale también en node:sqlite.

Si ves junto a tu base de datos ficheros que terminan en -wal y -shm, es que usa el modo WAL: forman parte de ella y, si copias a mano, se copian los tres juntos.

copia.py
import sqlite3

# Copia buena aunque el bot esté encendido y escribiendo.
origen = sqlite3.connect("datos.db")
destino = sqlite3.connect("copia.db")
with destino:
    origen.backup(destino)
destino.close()
origen.close()

Si tu bot está en NapNodes

  1. El fichero se queda en el disco del bot

    Lo que tu bot escribe en su carpeta sigue ahí al apagarlo, al reiniciarlo y al actualizar el código: lo ves en «Archivos». Para una base de datos de un bot sobra disco: el bot gratis tiene 1 GB, el Mini 2,5 GB y el Plus 5 GB, y una base de datos de puntos de miles de usuarios no llega a unos pocos megas.

  2. Si traes el código de GitHub

    Con la actualización en cada reinicio de «Configuración», el panel trae los cambios de tu repositorio. Con datos.db en .gitignore, como arriba, tu base de datos no se toca.

  3. Copias

    Con un plan, en «Copias de seguridad» haces copias de todo el bot (la base de datos incluida) y se hace sola una «Copia diaria automática» cada madrugada. El bot gratis no tiene copias: apágalo y descarga datos.db desde «Archivos» de vez en cuando.

  4. Antes de reinstalar o de borrar

    Descarga datos.db desde «Archivos» antes de «Reinstalar el servidor» (al final de «Configuración») o de borrar el bot. Y si tu bot es el gratis, renuévalo cada semana: sin renovar se apaga, y a los 14 días sin renovar se borra con todos sus ficheros (te avisamos antes).

Guarda solo lo que el bot necesita

Para puntos, niveles o avisos basta con el ID del usuario y del servidor de Discord: no hace falta guardar sus mensajes, ni su nombre (cambia), ni nada más. Cuanto menos guardes, menos tienes que proteger. Y recopilar datos de usuarios para otra cosa que no sea lo que hace tu bot va contra las normas de Discord y contra nuestra Política de Uso Aceptable.

Preguntas frecuentes

¿JSON o SQLite para un bot de Discord?

JSON para unos pocos ajustes que casi no cambian; SQLite para todo lo que cambia a menudo, como puntos, niveles o avisos. SQLite no se queda a medias si el bot se corta mientras escribe y viene con Python y con Node.js 22.13 en adelante.

¿Por qué mi bot pierde los datos al reiniciarse?

Porque los guarda en variables, que viven en memoria y se borran al reiniciar. Hay que escribirlos en un fichero, por ejemplo una base de datos SQLite junto al código.

¿Se borra la base de datos de mi bot al reiniciarlo en NapNodes?

No: los ficheros que el bot escribe en su carpeta se quedan en su disco y los ves en «Archivos». Antes de «Reinstalar el servidor» o de borrar el bot, descárgala.

Me sale «database is locked», ¿qué hago?

Otro programa tiene abierto el mismo fichero y está escribiendo. Lo más habitual es tener el bot encendido dos veces (en tu ordenador y en el hosting a la vez), o una transacción que no se cierra nunca: en Python, guarda con «with db:» o con commit() después de cada cambio.

¿Puedo usar MongoDB con mi bot?

Sí, si tu bot puede conectarse a ese servicio por internet. Compensa cuando varios programas tienen que compartir los datos; para un bot solo, SQLite es más sencillo y no depende de otra cuenta.

¿Te ha servido esta guía?

¿Lo montamos?

Hosting de bots de Discord desde 1,50 € al mes, sin sobreventa: tu RAM es solo tuya. Y si quieres empezar sin pagar, tienes un bot de Discord gratis, sin tarjeta.

Sigue leyendo

Todas las guías de Bots de Discord, ordenadas →