Your first command
Define, register, and run a slash command end-to-end.
The fastest path to a running bot is npx create-djs-commands — it scaffolds the directory layout, tsconfig.json, and .env.example for you. This page walks through the same setup by hand so you understand each piece.
Define a command
A command is just a plain object you build with defineCommand. The function is identity at runtime — it exists for IDE autocomplete and type inference.
import { defineCommand } from "@djs-commands/core";
export default defineCommand({
name: "ping",
description: "Replies with pong",
run: async ({ reply }) => {
await reply("pong");
},
});The handler receives a CommandRunContext with a unified reply() (works for both slash and legacy invocations), the invoking author, the guild, member, and typed options.
Wire up a Client
Pass your commands to createCommandHandler along with a discord.js Client. The handler subscribes to interactionCreate and dispatches matching slash commands. On clientReady, it registers the command list with Discord.
import { createCommandHandler } from "@djs-commands/core";
import { Client, GatewayIntentBits } from "discord.js";
import ping from "./commands/ping";
const token = process.env.DISCORD_TOKEN;
if (!token) {
console.error("DISCORD_TOKEN environment variable is required");
process.exit(1);
}
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
const handler = createCommandHandler({
client,
commands: [ping],
});
await handler.ready;
client.once("clientReady", (c) => {
console.log(`Logged in as ${c.user.tag}`);
});
await client.login(token);Or skip the manual import and let the framework load the directory for you:
createCommandHandler({
client,
commandDir: "./src/commands",
});In dev (NODE_ENV !== "production") the directory is watched and edited files hot-reload without a restart.
Run the bot
DISCORD_TOKEN=... bun run src/index.tsOnce the bot is online, run /ping in any guild it's invited to. You should see pong come back.
The first time you run a freshly registered command, Discord may take a minute to propagate it. Subsequent code edits with the same command name update instantly.
Next steps
- Add typed options — see Commands → Options.
- Gate execution with role/permission/channel checks — see Validators.
- Rate-limit with built-in cooldowns — see Cooldowns.
- Persist state (prefixes, disabled commands, your own data) — see Storage.
- Build rich replies with Components V2 — see Components V2.
- Extend the handler with a plugin — see Plugins.
Coming from v1?
If you're upgrading from @d3oxy/djs-commands, the Migration from v1 guide walks through every API that moved or was dropped, with side-by-side examples.