[Bot Name]
A Discord bot. Slash commands, buttons, modals. TypeScript. Runs on a single VM.
Source of truth
Code on GitHub. Deployed to a tiny Fly.io VM (shared-cpu-1x, 256MB is plenty for most bots). The deployed binary is what answers Discord events.
Tech stack
Node 22 + TypeScript + discord.js 14. Slash commands registered via the Discord REST API on deploy. Storage in a SQLite file (better-sqlite3) for v0; swap to Postgres when you need multi-instance. Logs to Pino + Fly logs.
Deploy
- Slash command sync:
npm run register(one-off when commands change) - Deploy:
fly deployfrom local - Logs:
fly logs
File map
src/index.tsclient setup, event router, loginsrc/commands/one file per slash command (/ping.ts,/setup.ts)src/interactions/button + modal handlerssrc/events/Discord event handlers (messageCreate,guildCreate)src/db.tsbetter-sqlite3 wrappersrc/lib/logger.tsPino setupscripts/register-commands.tsPOSTs slash command schemas to Discordfly.toml
.env keys
DISCORD_TOKENbot token from Developer PortalDISCORD_CLIENT_IDapplication IDDISCORD_GUILD_IDyour test server (for fast iteration). Remove for global commands in prod.DATABASE_PATHdefaults./data/bot.db
Hard rules
- Required gateway intents declared explicitly.
GUILDS+ whatever else you actually need. Don't requestMESSAGE_CONTENTunless you use it (Discord asks for verification above 100 guilds). - Slash commands have a 3-second initial response window. If your handler does work, use
interaction.deferReply()immediately andeditReply()later. - Every command file exports
{ data, execute }. The router auto-loads them. No manual switch statements. - DB writes are synchronous (better-sqlite3 is sync); that's fine for a single-instance bot. Don't add async if you don't need it.
- Bot token never committed.
.envis.gitignored. - Use ephemeral replies (
flags: MessageFlags.Ephemeral) for anything per-user.
Recent significant changes
- 2026-04-30: Scaffolded. Locked: discord.js 14 (still the boring choice), better-sqlite3 over knex/Drizzle (single-file simplicity), Fly.io over Railway (cheaper at sleep).
Next session: start here
- Create application at
https://discord.com/developers/applications. SaveDISCORD_CLIENT_ID. - Add bot, copy
DISCORD_TOKENinto.env. - OAuth URL Generator -> scopes:
bot applications.commands. Pick permissions. Invite to test server. npm run registerto upload slash commands.npm run devto test locally beforefly deploy.