ESN Docs

Docs › Discord bots

How to add a SQLite database to your Discord bot (discord.py and discord.js)

2 min readUpdated Nov 3, 2026

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.db from 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. aiosqlite from requirements.txt and better-sqlite3 from package.json are 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.

Host your bot on ESN. ESN bot servers run Python or Node.js 24/7 with a live console and file manager. Bot hosting from $0.50/month. See bot plans · or ask in the ESN Systems Discord.

Still stuck? Use the live chat on the panel or ask in the ESN Systems Discord. Last updated November 3, 2026.