Spraxium logoSpraxium

Build Discord bots
like real software.

Spraxium adds the architecture layer that Discord.js doesn't provide: modules, dependency injection, guard pipelines, and typed commands. One model, every interaction type.

Code editor preview showing a Spraxium bot project
src/main.ts
1import { IntentPreset, SpraxiumFactory } from '@spraxium/core';
2import { EnvValidator } from '@spraxium/env';
3import { Logger } from '@spraxium/logger';
4import { AppEnv } from './app.env';
5import { AppModule } from './app.module';
6
7async function main() {
8 const logger = new Logger('Main');
9 const environment = EnvValidator.validate(AppEnv);
10
11 const app = await SpraxiumFactory.create({ token: environment.token });
12 app.useModule(AppModule);
13 app.provide(AppEnv, environment);
14 app.intents(IntentPreset.Standard);
15
16 logger.info('Starting application...');
17 await app.listen();
18}
19
20main();

Modules with explicit boundaries, injected providers, and a DI container that resolves everything deterministically at boot.

Stack modules across feature boundaries, share providers between them, and apply guards to every interaction type from a single decorator.

Every provider hooks into onBoot(), onReady(), and onShutdown(). No hidden initialization order, no timing races.

Write permission logic once as a guard class and apply it to slash commands, context menus, and prefix commands with a single decorator.

Every command option, interaction payload, and provider contract is fully typed. No casting, no any, no runtime surprises from Discord's API.

Providers are pure injectable classes with declared dependencies. Swap real implementations for mocks at test time without touching production code.

Architecture benchmark

Less complexity as your bot grows.

As a project scales past a few commands, discord.js requires increasingly manual wiring: guards, DI, scheduling, and state all done by hand. Spraxium handles that layer.

Spraxium
Sapphire
discord.js
Complexity0255075100Hello World5 CommandsServicesProduction

* Illustrative complexity estimates based on lines of infrastructure code required per milestone in a typical production bot project.

The Architecture Gap

Same command. Different world.

Same /ban command, built twice. Once with raw discord.js, once with Spraxium. The difference is not syntax. It is structure, safety, and scale.

1// 1. Register the slash command with Discord
2const { REST, Routes, SlashCommandBuilder } = require('discord.js');
3
4const command = new SlashCommandBuilder()
5 .setName('ban')
6 .setDescription('Ban a member from the server.')
7 .addUserOption(opt =>
8 opt.setName('user').setDescription('Target').setRequired(true))
9 .addStringOption(opt =>
10 opt.setName('reason').setDescription('Reason'));
11
12const rest = new REST().setToken(process.env.DISCORD_TOKEN);
13await rest.put(Routes.applicationCommands(CLIENT_ID), {
14 body: [command.toJSON()],
15});
16
17// 2. Handle the interaction at runtime
18client.on('interactionCreate', async (interaction) => {
19 if (!interaction.isChatInputCommand()) return;
20 if (interaction.commandName !== 'ban') return;
21
22 if (!interaction.memberPermissions?.has('BanMembers')) {
23 return interaction.reply({ content: 'No permission.', flags: 'Ephemeral' });
24 }
25
26 const target = interaction.options.getUser('user', true);
27 const reason = interaction.options.getString('reason') ?? 'No reason';
28 const member = interaction.guild?.members.cache.get(target.id);
29
30 if (!member) {
31 return interaction.reply({ content: 'User not found.', flags: 'Ephemeral' });
32 }
33
34 await member.ban({ reason });
35 await interaction.reply(`Banned ${target.tag}.`);
36});
  • Registration and handler are completely decoupled, with no structural link between them
  • Permission checks written by hand in every handler with no enforcement or reuse
  • All interactions route through one global listener, zero feature isolation
  • No dependency injection; services are imported directly with no abstraction
  • No module system; features grow side by side with no enforced boundaries

Why it exists

Built out of real frustration.

Every Spacelaxy bot started the same way: a clean discord.js project that turned into a tangled mess as features were added. No structure, no boundaries, global state everywhere. The code worked, but nobody wanted to touch it.

Spraxium started as an internal tool to fix that. Modules, DI, guards: principles borrowed from backend engineering, applied to Discord bot development. It ran every Spacelaxy bot in production for over a year before anyone outside the team saw it.

Now it’s open source. The same framework, cleaned up, documented, and ready for anyone who’s tired of writing the same if-else chains in every listener.

Henry Thierry
Henry Thierry
Founder, Spacelaxy • Creator of Spraxium

Slash commands

Define the shape. Handle the logic. Keep them separate.

Spraxium splits slash commands into two files: a command class that declares options and permissions, and a handler that receives injected services and executes the logic. The framework wires them together at boot.

  • Type-safe options via @SlashOption decorators
  • Full DI injection in the handler, just like any other class
  • Guards apply at the handler level, not buried inside it
  • Auto-registered on Discord at startup
src/modules/slash/commands/ban.command.ts
1import { SlashCommand, SlashOption } from '@spraxium/common';
2import { PermissionFlagsBits } from 'discord.js';
3
4@SlashCommand({
5 name: 'ban',
6 description: 'Ban a member from the server.',
7 defaultMemberPermissions: PermissionFlagsBits.BanMembers,
8})
9export class BanCommand {
10 @SlashOption.User('target', {
11 description: 'The member to ban',
12 required: true,
13 })
14 @SlashOption.String('reason', {
15 description: 'Reason for the ban',
16 required: false,
17 })
18 build() {}
19}

Package ecosystem

One framework, twelve packages.

Twelve focused packages built around the same module-and-DI contract. Each is opt-in, independently versioned, and drops into any module with a single import. Start with core and common. Add the rest as your bot grows.

UI Components

Build Discord UI components as TypeScript classes.

Declare each Discord UI component as a typed TypeScript class. No manual builders, no scattered options. Composable and injectable out of the box.

Explore Components
src/modules/moderation/schemas/buttons.ts
1import { Button, LinkButton } from '@spraxium/components';
2
3@Button({
4 customId: 'mod:ban-confirm',
5 label: 'Confirm Ban',
6 style: 'danger',
7 emoji: '🔨',
8})
9export class BanConfirmButton {}
10
11@Button({
12 customId: 'mod:ban-cancel',
13 label: 'Cancel',
14 style: 'secondary',
15})
16export class BanCancelButton {}
17
18@LinkButton({
19 label: 'View User',
20 url: 'https://discord.com/users/...',
21 emoji: '🔗',
22})
23export class ViewUserButton {}

Modals

Modals that feel native, validated, and production-ready.

Define modal fields as class properties. Validators run before your handler is called. Cached responses survive the 15-minute Discord timeout automatically.

Read the docs
1import {
2 ModalCache,
3 ModalComponent,
4 ModalInput,
5 ModalValidate,
6 minWords,
7 minLength,
8} from '@spraxium/components';
9
10@ModalCache({ ttl: 300 })
11@ModalComponent({ id: 'report', title: 'Report an issue' })
12export class ReportModal {
13 @ModalValidate([minWords(5)])
14 @ModalInput({
15 label: 'What happened?',
16 style: 'paragraph',
17 required: true,
18 maxLength: 1000,
19 })
20 description!: string;
21
22 @ModalValidate([minLength(3)])
23 @ModalInput({
24 label: 'Evidence link',
25 placeholder: 'https://…',
26 })
27 evidence!: string;
28}

CLI Ecosystem

One command. Any integration.

Spraxium's CLI doesn't stop at installing dependencies. It scaffolds providers, registers modules, and wires defaults into your architecture in seconds. Click any integration to preview the exact command and generated setup.

Spraxium

Use cases

One framework. Every kind of bot.

From scalable slash command bots to multi-service architectures, each package slots in independently and does one job well.

Scalable bot architecture

Build multi-module bots with topological DI, guard pipelines, and typed lifecycle hooks. @spraxium/core and @spraxium/common handle structure that grows from 10 to 200 commands.

HTTP endpoints inside your bot

Run REST controllers in the same process as Discord. @spraxium/http provides DTOs, validation, and a BotBridge to send Discord messages directly from HTTP handlers.

Cross-service event delivery

Receive typed, HMAC-signed events from any Node.js backend using @spraxium/signal. No polling, no shared database, no coupling between services.

Scheduled jobs and automation

Declare cron and interval jobs that start after onReady() and stop cleanly on shutdown. @spraxium/schedule integrates with the module lifecycle out of the box.

Multi-language community bots

Per-guild locale resolution, lazy-loaded message files, and typed translation keys across every slash command. Powered by @spraxium/i18n.

Typed config and observability

Validate every environment variable at boot with auto-generated schemas. Track command usage, latency, and error rates from day one with @spraxium/env and @spraxium/analytics.

Real feedback from beta testers · pre-launch

“I've tried discord.js, Necord, even raw REST calls. Nothing comes close. Spraxium is the first framework that actually feels like it was designed for large bots. Necord couples too hard with discord.js internals and bleeds that complexity everywhere. With Spraxium, each feature finally has a real home.”

Smoker's avatar
SmokerBot developer · beta tester

Project Status

What's shipped. What's next.

Spraxium is in active development. Here's a transparent overview of what's available today and what's on the roadmap.

Overall progress26 / 35
Completed
26
  • @spraxium/logger: structured logging
  • @spraxium/webhook: decorator-based senders
  • @Defer() and @AutoDefer() decorators
  • Process lock & --force-unlock flag
  • Upgrade notifier on startup
  • Cross-package stabilization (0.2.0)
  • Webhook handler decorators
  • Hot module reload (dev mode)
In Progress
3
  • Documentation site
  • Testing utilities & vitest helpers
  • Example bot projects
Upcoming
6
  • @spraxium/dashboard: web UI
  • Plugin registry & marketplace
  • Voice state handler decorators
  • CLI new project templates v2
  • Rate limiting decorators
  • @spraxium/cache: distributed cache

Start building

Treat your bot as a product surface.

Clear module boundaries, a guard pipeline that covers every interaction type, startup diagnostics that show exactly what loaded, and nine packages ready when your bot needs to grow.