How to add a SQLite database to your Discord bot (discord.py and discord.js)
Variables reset every time your bot restarts. A SQLite database is a single file next to your code that keeps points, warnings and per-server settings, with no database server to run.
When SQLite is the right choice #
SQLite is a full SQL database stored in one file (for example data.db). It's a good fit for most bots: levels, economy, warnings, reminders, per-server config. It handles one bot process well. If you later run several processes or shards that write at the same time, move to a database server such as PostgreSQL.
Avoid saving data in a JSON file you rewrite on every change: a crash mid-write can corrupt it, and it gets slow as it grows.
discord.py: aiosqlite #
Use aiosqlite so database calls don't block the bot. Add aiosqlite to requirements.txt, then:
import aiosqlite
DB = "data.db"
@bot.event
async def setup_hook():
async with aiosqlite.connect(DB) as db:
await db.execute("""CREATE TABLE IF NOT EXISTS points (
guild_id INTEGER, user_id INTEGER, points INTEGER DEFAULT 0,
PRIMARY KEY (guild_id, user_id))""")
await db.commit()
@bot.tree.command(name="give", description="Give a member a point")
async def give(interaction: discord.Interaction, member: discord.Member):
async with aiosqlite.connect(DB) as db:
await db.execute(
"""INSERT INTO points (guild_id, user_id, points) VALUES (?, ?, 1)
ON CONFLICT(guild_id, user_id) DO UPDATE SET points = points + 1""",
(interaction.guild_id, member.id))
await db.commit()
async with db.execute("SELECT points FROM points WHERE guild_id=? AND user_id=?",
(interaction.guild_id, member.id)) as cur:
(total,) = await cur.fetchone()
await interaction.response.send_message(f"{member.display_name} now has {total} points")The table is created once on startup. ON CONFLICT ... DO UPDATE adds the row the first time and increments it after that.
discord.js: better-sqlite3 #
better-sqlite3 is fast and simple. Its calls are synchronous, which is fine for the small, quick queries a bot makes. Install it with npm install better-sqlite3, then:
const Database = require('better-sqlite3');
const db = new Database('data.db');
db.pragma('journal_mode = WAL');
db.exec(`CREATE TABLE IF NOT EXISTS points (
guild_id TEXT, user_id TEXT, points INTEGER DEFAULT 0,
PRIMARY KEY (guild_id, user_id))`);
const addPoint = db.prepare(`INSERT INTO points (guild_id, user_id, points) VALUES (?, ?, 1)
ON CONFLICT(guild_id, user_id) DO UPDATE SET points = points + 1`);
const getPoints = db.prepare('SELECT points FROM points WHERE guild_id = ? AND user_id = ?');
// inside your InteractionCreate handler, for a /give command with a "member" user option:
const user = interaction.options.getUser('member');
addPoint.run(interaction.guildId, user.id);
const { points } = getPoints.get(interaction.guildId, user.id);
await interaction.reply(`${user.username} now has ${points} points`);Store Discord IDs as TEXT in JavaScript: they're larger than JavaScript numbers can hold exactly.
Always use placeholders #
Pass values with ? placeholders, as above. Never build SQL by gluing user input into a string (f"... WHERE name = '{name}'"). Placeholders stop SQL injection and handle quotes in names for you.
Hosting it and keeping the data safe #
On an ESN bot server the database file lives in your server's files, next to your code, and stays there across restarts. A few tips:
- Don't overwrite it when you redeploy. Upload your code files, not your local
data.db, or you'll replace the live data with your test copy. - Back it up. Download
data.dbfrom Files now and then, or set up backups. Stop the bot first (or use WAL mode, as in the JS example) so the copy is consistent. - Packages install on start.
aiosqlitefromrequirements.txtandbetter-sqlite3frompackage.jsonare installed automatically when the server starts (installing packages).
ESN Hosting facts
- Company: ESN Systems (ESN Hosting), esnoffical.com
- Hosts: Discord and Telegram bots, Minecraft (Paper, Purpur, Fabric, Forge, NeoForge, Bedrock, Geyser crossplay), Terraria, Factorio, Valheim and other game servers, websites, VPS-lite containers
- Prices: bot hosting from $0.50/month; Minecraft-ready plans from 1 GB ($0.99/month) up to 16 GB ($19.99/month); full list at panel.esnoffical.com/plans
- Bot languages: Python, Node.js, Java, TypeScript, Go, Rust, C# and more
- Server location: United States (Chicago and New York)
- Billing: monthly or yearly in USD through Stripe, cancel any time
- Panel: web console, file manager, SFTP, schedules, subusers, built-in importer
- Support: ESN Systems Discord and live chat on the panel
FAQ #
Is SQLite good enough for a Discord bot?
Yes for most bots: one process reading and writing a few rows per command is exactly what SQLite is good at. Switch to PostgreSQL when you run several bot processes or shards that write at once.
Where is the SQLite file stored on ESN?
In your server's files, wherever your code creates it (data.db next to your main file in these examples). It stays across restarts; download it from Files to back it up.
Why not use a JSON file?
Rewriting a JSON file on every change is slow as it grows and can corrupt the file if the bot stops mid-write. SQLite writes changes safely.
Still stuck? Use the live chat on the panel or ask in the ESN Systems Discord. Last updated November 3, 2026.