SWAGcommands
Start here

Quick start

Build your first slash command with TypeScript.

Let's make /ping. You'll need a Discord application and bot token from the Discord Developer Portal, plus Node.js and a package manager. The examples open on pnpm; you can switch to Yarn, Bun, or npm. Invite the bot to a test server with the bot and applications.commands scopes.

1. Start a project

Create a project folder, then choose your package manager:

mkdir my-bot && cd my-bot
pnpm init
pnpm add swagcommands discord.js
pnpm add -D typescript @types/node

SWAGcommands v2 publishes a CommonJS package. This guide compiles TypeScript to CommonJS so the imports below work directly. Add scripts to package.json:

package.json
{
  "scripts": {
    "build": "tsc",
    "start": "node dist/index.js"
  }
}

Create tsconfig.json:

tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "CommonJS",
    "moduleResolution": "Node",
    "esModuleInterop": true,
    "strict": true,
    "rootDir": "src",
    "outDir": "dist",
    "skipLibCheck": true
  },
  "include": ["src"]
}

2. Add a command

Create src/commands/ping.ts:

src/commands/ping.ts
import { CommandType } from "swagcommands";
import type { CommandObject } from "swagcommands";

const ping = {
  type: CommandType.SLASH,
  description: "Check if the bot is awake",
  callback: () => "Pong!",
} satisfies CommandObject;

export default ping;

The filename supplies the command name. ping.ts becomes /ping. For slash commands, description is required.

3. Connect the client

Create src/index.ts:

src/index.ts
import { once } from "node:events";
import { Client, Events, GatewayIntentBits } from "discord.js";
import SWAG from "swagcommands";

async function main() {
  const token = process.env.DISCORD_TOKEN;
  if (!token) throw new Error("Set DISCORD_TOKEN before starting the bot.");

  const client = new Client({ intents: [GatewayIntentBits.Guilds] });
  const swag = await SWAG.create({
    client,
    commandsDir: "./dist/commands",
  });

  await client.login(token);
  if (!client.isReady()) await once(client, Events.ClientReady);

  // Sync the local slash command list with Discord.
  await swag.deployCommands();
  console.log(`Ready as ${client.user?.tag}`);
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

Build and run:

pnpm build
DISCORD_TOKEN=your-token pnpm start

Try /ping in your test server. Keep the token out of source control and logs. A local environment loader is fine too; it just needs to populate process.env.DISCORD_TOKEN before main() runs.

Deployment replaces the target command list

deployCommands() sends a complete manifest to Discord. Commands in that target scope that are missing from your local files are removed. Use a dedicated test application or be deliberate about which process owns the scope.

Next, learn how to add arguments, choose a deployment scope, or shut down features cleanly.

On this page