Choose around your data
Start with what the bot needs to remember. Server settings, subscriptions, and item ownership have different requirements from a temporary command cache. Write down the reads and updates your busiest command performs before comparing databases.
| Option | A useful fit | Main trade-off |
|---|---|---|
| PostgreSQL | Related records, purchases, inventories | You manage a server or pay for a managed service |
| MongoDB | Documents you usually read and update together | Flexible fields still need a deliberate data model |
| SQLite | A bot running on one machine | One writer at a time; local storage must persist |
| Supabase | PostgreSQL plus a web dashboard or app | Platform services add quotas and access rules to manage |
our starting point
Use the simplest database that meets the workload.
SQLite is a reasonable starting point for a small single-host bot. PostgreSQL is a useful default when several processes share durable data. Neither choice depends on how impressive your server count looks.
What each option gives you
PostgreSQL
MongoDB
SQLite
Supabase
Model the common bot operations
Keep Discord IDs as strings in JavaScript. They can exceed the safe integer range. Use a unique server ID for settings, and an event or transaction ID to make repeated webhooks safe to process.
| Operation | What to protect |
|---|---|
| Save server settings | Only authorized staff can update that server’s record |
| Transfer virtual credits | Both balance changes succeed together or neither does |
| Grant a purchased feature | A repeated event does not grant it twice |
| Record command activity | A retention policy prevents unlimited growth |
| Delete a user’s data | Related records can be located and removed |
Use parameterized queries. Add indexes for queries you actually run, and inspect slow queries before increasing the machine size. Keep connection pools small enough that all bot processes together fit the database limit.
Plan for restarts and mistakes
- Back up durable data and test restoring it into a separate database.
- Run schema changes deliberately; keep old and new code compatible during deployment.
- Keep a database timeout so a stuck query does not hold commands open forever.
- Treat caches as replaceable copies, not the only record of purchases.
- Monitor storage, connection use, and slow queries as traffic grows.
A database migration becomes easier when queries are kept behind a small data-access layer. Avoid scattering raw database calls through every command handler. See the scaling guide when one process is no longer enough.