A sane, opinionated template for discord bots written in typescript using the discord.js library.
For new, node lts (currently v24) projects.
Uses:
- biome for linting and formatting
- commitlint for linting commit messages
- husky for git hooks
- lint-staged for checks on commit
- vitest for testing
- tsx for dev time typescript
- varlock for env validation and parsing
- tsdown for building
- variables
DISCORD_CLIENT_ID,DISCORD_CLIENT_TOKEN, andLOG_LEVELfilled in.envfile (see.env.schema) - a notion of what a discord bot is and how
discord.jsworks - a server to test the bot on
Runs the project in watch mode.
Builds/transpiles the code to ./build.
Runs the built project.
Runs tests with vitest.
Runs biome in fix mode (only safe fixes) to lint and format the project.
Runs type checking using tsc.
Commands live in ./src/commands/ as individual files and are registered in ./src/modules/commands.ts.
Note: Discord requires command names to be lowercase. Use kebab-case for multi-word commands (e.g., my-command).
- Create a new file in
./src/commands/(e.g.,my-command.ts) - Export a typed
BotCommandobject withdataandexecute:
import { type CommandInteraction, SlashCommandBuilder } from "discord.js";
import type { BotCommand } from "#/modules/commands";
async function execute(interaction: CommandInteraction): Promise {
await interaction.reply("Hello!");
}
export const myCommand: BotCommand = {
data: new SlashCommandBuilder().setName("my-command").setDescription("Does something"),
execute,
};- Import and add the command to the
commandsMap in./src/modules/commands.ts:
import { myCommand } from "#/commands/my-command";
export const commands = new Map([[myCommand.data.name, myCommand]]);- Run
pnpm cmd register globalto register commands globally orpnpm cmd register 0000000000to a specific server - Restart your bot
You can also unregister commands with pnpm cmd unregister global or pnpm cmd unregister 0000000000.
You might want to install the recommended extensions in vscode. Search for @recommended in the extensions tab, they'll show up as "workspace recommendations".
If you have been using eslint and prettier and their extensions, you might want to disable eslint entirely and keep prettier as the formatter only for certain types of files.
This is done by the .vscode/settings.json file.
Debug configurations are also included (for source using tsx and for bundle using the generated source maps).