A modern, type-safe Discord.js framework with decorators and advanced features.
Decorated classes are the primary HarmonixJS API. Create a typed decorator definition once, then reuse it for the class and its inferred handler:
constGuildCreated=Event(Events.GuildCreate);
@GuildCreatedexportdefaultclassGuildCreatedEvent{execute=GuildCreated.handler(async(bot,guild)=>{// guild is inferred as Guild});}constPing=Command({name: "ping",description: "Ping",type: "prefix"});
@PingexportdefaultclassPingCommand{execute=Ping.handler(async(bot,ctx)=>{// ctx is inferred as CommandContext<"prefix">});}No implements EventExecutor<...> or implements CommandExecutor<...> is
required. The defineEvent, defineCommand, and defineComponent helpers
remain available as a secondary functional API.
Application events are declared once through module augmentation:
declare module "@harmonixjs/core"{interfaceHarmonixCustomEvents{"guild:configured": [guildId: string,enabled: boolean];}}bot.events.on("guild:configured",(bot,guildId,enabled)=>{// bot is always injected as the first argument});awaitbot.events.emitAsync("guild:configured",guild.id,true);Providers use the same typed registry pattern:
declare module "@harmonixjs/core"{interfaceHarmonixProviderRegistry{notifications: NotificationProvider;}}constbot=newHarmonix({// ...providers: [newNotificationProvider()]});bot.providers.notifications.send();Harmonix can be used from JavaScript projects. TypeScript remains recommended for the typed decorators, plugin registries, and provider registries.
- 🎯 TypeScript-friendly with full type safety when you want it
- 🎨 Decorator-based commands and events
- 📥 Automatic imports so you write less and code faster
- 🔌 Plugin system for extensibility
- 🚀 Easy to use with minimal setup
npm install @harmonixjs/core tsx discord.jsnpx tsc --initUpdate tsconfig.json:
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "node",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"outDir": "dist",
"esModuleInterop": true
}
}// src/index.tsimport{Harmonix}from"@harmonixjs/core";import{DatabasePlugin}from"@harmonixjs/quick-db";interfaceUser{id: string;coins: number;}constbot=newHarmonix({bot: {id: "YOUR_BOT_CLIENT_ID",token: "YOUR_BOT_TOKEN"},publicApp: true,folders: {commands: "./src/commands",events: "./src/events",components: "./src/components"},plugins: [newDatabasePlugin()],intents: [3249151]// (All Intents)});// The plugin name and type are inferred automatically.constusers=bot.plugins.database.table<User>("users");// Start listeningbot.start();// src/commands/Ping.tsconstPing=Command({name: "ping",description: "Ping command"});
@PingexportdefaultclassPingCommand{execute=Ping.handler(async(bot,ctx)=>{awaitctx.reply(`Pong! ${bot.ws.ping}ms`);});}// src/events/Ready.tsconstReady=Event(Events.ClientReady);
@ReadyexportdefaultclassClientReady{execute=Ready.handler((bot,client)=>{bot.logger.sendLog("SUCCESS",`Bot is ready! Logged in as ${client.user.tag}`);});}// src/components/TestButton.tsconstTestButton=Component({id: "test_button"});
@TestButtonexportdefaultclassTestButtonComponent{execute=TestButton.handler(async(bot,ctx)=>{awaitctx.reply("Test button clicked!");});}JavaScript projects can use the functional helpers instead of decorator syntax:
// src/commands/ping.jsconst{ defineCommand }=require("@harmonixjs/core");module.exports=defineCommand({name: "ping",description: "Ping command"},async(bot,ctx)=>{awaitctx.reply(`Pong! ${bot.ws.ping}ms`);});npx tsx src/index.tsHarmonix supports first-class plugins — you can add plugins directly to the framework to register commands, events, middleware, or extend internals.
Official and third-party plugins can augment the typed registry:
exportclassMyPluginimplementsHarmonixPlugin{readonlyname="myPlugin"asconst;init(bot: Harmonix){// Initialize the plugin.}}declare module "@harmonixjs/core"{interfaceHarmonixPluginRegistry{myPlugin: MyPlugin;}}Applications can then use bot.plugins.myPlugin without a generic, optional chaining, or a non-null assertion. Accessing a plugin that was not registered throws an explicit runtime error.
Harmonix forwards Discord.js application-command options directly:
import{ApplicationCommandOptionType,ApplicationCommandType,ApplicationIntegrationType,InteractionContextType}from"discord.js";
@Command({name: "profile",description: "Display a profile",contexts: [InteractionContextType.Guild,InteractionContextType.BotDM,InteractionContextType.PrivateChannel],integrationTypes: [ApplicationIntegrationType.GuildInstall,ApplicationIntegrationType.UserInstall],options: [{name: "user",description: "The user to display",type: ApplicationCommandOptionType.User}]})User and message context commands are also supported:
@Command({name: "View profile",type: "user",applicationType: ApplicationCommandType.User})- @harmonixjs/quick-db: Simple and flexible Quick.db plugin for Harmonix Discord framework.
- @harmonixjs/express: A powerful Express-based HTTP API plugin for the Harmonix Discord framework.
- @harmonixjs/i18n: Runtime translations and Discord command localization.
- @harmonixjs/shard: Automatic multi-process sharding using Discord's recommended shard count.
- @harmonixjs/image-builder: Development..