Introduction
discord.js++ — a developer-experience-first layer over discord.js. spearkit re-exports all of discord.js (so it's a drop-in replacement) and adds an ergonomic, fully type-safe API…
Contents
- Getting started — install, first bot, project layout.
- Client —
SpearClient, intents,register,start, deployment. - Commands — slash commands, subcommands, permissions, deployment.
- Options — typed option builders, choices, autocomplete.
- Components — buttons, selects, modals, custom-id routing.
- Events — the
event()helper and the event registry. - Contexts — reply helpers shared by every handler.
- Cooldowns — per-user/role/guild rate limiting.
- Scheduled tasks — cron and interval jobs.
- Prefix commands — classic
!textcommands. - Logging — structured, leveled, scoped logging.
- Usage tracking — record who used what (store + Discord channel).
- Environment & dotenv — load
.envand read typed env vars. - Plugins — bundling features into reusable units.
- File-based loading — one file per command/event/component.
- Migrating from discord.js — the drop-in path.
- API reference — every exported symbol.
Why spearkit
- Drop-in.
import { Client, EmbedBuilder } from "spearkit"— every discord.js export is available, so you can migrate one file at a time. - Fully type-safe. No
anyorunknownleaks into your handlers. Option values, custom-id params and modal fields are all inferred from your definitions. - Co-located. A command's options and handler, a button's appearance and click logic, a modal's fields and submit logic — each lives in one place.
- No boilerplate. No
interactionCreateswitch statements; spearkit routes commands, autocomplete, buttons, selects and modals for you.
Thirty-second tour
import { SpearClient, Intents, command, option, button, row, event } from "spearkit";
const client = new SpearClient({ intents: Intents.default });
const greet = command({
name: "greet",
description: "Greet someone",
options: { who: option.user({ description: "Who", required: true }) },
run: (ctx) => ctx.reply(`Hello ${ctx.options.who}!`), // who: User
});
const ping = button({
id: "ping:{n}",
label: "Ping",
run: (ctx) => ctx.update(`pong #${ctx.params.n}`), // n: string
});
client.register(greet, ping, event("clientReady", (c) => console.log(c.user.tag)));
await client.start(process.env.DISCORD_TOKEN);
await client.deployCommands({ guildId: process.env.GUILD_ID });