Root Development Experience β A clean, file-system based framework for building Root applications with commands, events, jobs and services.
Retrieve your Application ID & Dev Token at https://dev.rootapp.com/apps
- π― Command System - Auto-discovered commands with validation, cooldowns and usage
- π‘ Event Handlers - Type-safe event listeners
- β° Job Scheduler - Scheduled background tasks
- π File-System Based - Keep your workspace clean and scalable
- π‘οΈ Type Safety - Full TypeScript support
- π Quick Start - Create a new project with
npx create-rdx@latest
npx create-rdx@latest --app my-app
cd my-app
pnpm run dev:serverpnpm add rdx.js @rootsdk/server-appNote:
@rootsdk/server-appis required and must be installed alongside rdx.js.
// server/src/main.ts
import { RDXServerApp } from "rdx.js";
const app = new RDXServerApp({
cmdPrefix: "!",
onReady: () => console.log("App ready!"),
});const app = new RDXServerApp({
cmdPrefix: "!", // Command prefix - Note: You can use an async function here to dynamically retrieve it
// cmdPrefix: async () => {
// return await rootServer.dataStore.appData.get("prefix");
// }
commandsFolderName: "commands", // Commands folder name (default: commands)
eventsFolderName: "events", // Events folder name (default: events)
jobsFolderName: "jobs", // Jobs folder name (default: jobs)
disableHelpCommand: false, // Disable built-in !help (default: false)
services: [], // Proto services
onStarting: async () => {}, // Called before init
onReady: () => {}, // Called after init
});The SDK auto-discovers files from any matching folder:
server/src/
main.ts
commands/
ping.ts
events/
welcome.ts
jobs/
backup.ts
modules/
automod/
events/
on-message-create.ts
mod-tools/
commands/
ban.ts
kick.ts
Create a command in server/src/commands/:
// server/src/commands/ping.ts
import { RootCommand, type CommandContext } from "rdx.js";
export default class PingCommand extends RootCommand {
constructor() {
super({
name: "ping",
description: "Ping the server",
aliases: ["p"],
cooldown: 1, // number in seconds
});
}
async execute({ ctx }: CommandContext): Promise<void> {
await ctx.reply("π Pong!");
}
}super({
name: "ping", // Command name (required)
description: "Ping", // Command description (required)
aliases: ["p", "pong"], // Alternative names (optional)
args: [
// Command arguments (optional)
{
name: "message",
description: "Message to send",
required: true, // If true, validation fails without it
options: ["a", "b"], // Restrict to specific values (optional)
},
],
usage: "ping [message]", // Custom usage string (optional)
examples: ["ping", "ping hello"], // Example usages (optional)
category: "Utility", // Command category (optional, default: "General")
cooldown: 3, // Cooldown in seconds (optional, default: 0)
});Commands automatically have helper methods available on ctx:
async execute({ ctx, args }: CommandContext): Promise<void> {
// Reply to the command message
await ctx.reply("Simple reply");
await ctx.reply("Reply with mention", { includeMention: true });
// Get user info
const nickname = await ctx.getMemberNickname();
const mention = await ctx.mention(); // [@Nickname](root://user/123)
const userId = ctx.member.id;
// Access command arguments
const firstArg = args[0];
}Create an event in server/src/events/:
// server/src/events/welcome.ts
import { RootEvent, RootEventType, type EventContext } from "rdx.js";
import { rootServer, type ChannelMessageCreatedEvent } from "@rootsdk/server-app";
export default class WelcomeEvent extends RootEvent<ChannelMessageCreatedEvent> {
constructor() {
super({
event: RootEventType.ChannelMessageCreated,
});
}
async execute({ event }: EventContext<ChannelMessageCreatedEvent>): Promise<void> {
// Helper methods are available on the event object
await event.reply(`Welcome! You said: ${event.messageContent}`);
await rootServer.community.channelMessages.create({
channelId: event.channelId,
content: "Hello!",
});
}
}super({
event: RootEventType.ChannelMessageCreated, // Event type (required)
once: false, // Run only once (optional, default: false)
enabled: true, // Enable/disable event (optional, default: true)
});Create a job in server/src/jobs/:
// server/src/jobs/daily-backup.ts
import { RootJob, type JobContext } from "rdx.js";
import { JobInterval } from "@rootsdk/server-app";
export default class DailyBackup extends RootJob {
constructor() {
super({
tag: "daily-backup",
resourceId: "system",
start: new Date(),
jobInterval: JobInterval.Daily,
});
}
async execute(context: JobContext): Promise<void> {
console.log("Running daily backup...");
// Access rootServer from context or import
await context.rootServer.community.communities.get({
communityId: "...",
});
}
}Check /examples directory for:
- Full-stack app with client + server
- Command examples with validation
- Event handlers
- Background jobs
- Node.js >= 22
@rootsdk/server-app(peer dependency)- Root Application ID & Dev Token from https://dev.rootapp.com/apps
MIT
Built for Root | Type-Safe | Easy