Client
The Client class (exported as ERXClient) is the entry point of syntx.js. It wraps a regular discord.js Client and adds a declarative layer for text commands, slash commands, events, and component interactions.
Note
syntx.js exports this class as ERXClient, not Client, so it never collides with discord.js's own Client if you import both.
const { ERXClient } = require("syntx.js");Constructor
new ERXClient(options);Options
| Option | Type | Required | Description |
|---|---|---|---|
token | string | Yes | Your bot token from the Discord Developer Portal. |
intents | number[] | number | Yes | Gateway intents to enable. Use the Intents helper. |
prefix | string | No | Prefix for text commands. Only required if you use command(). |
clientId | string | No | Your application/client ID. Only required if you use slash(). |
database | object | No | A SyntxDB instance or any object exposing your own database methods. |
const { ERXClient, Intents } = require("syntx.js");
const client = new ERXClient({
token: process.env.TOKEN,
intents: [Intents.Guilds, Intents.GuildMessages, Intents.MessageContent],
prefix: "!",
clientId: "YOUR_APPLICATION_ID",
});Warning
token and intents are validated as soon as the constructor runs. If either is missing, syntx.js throws a SyntxError immediately instead of letting the bot fail later when it tries to connect.
About intents
syntx.js re-exports a typed Intents helper so you don't have to import GatewayIntentBits from discord.js yourself.
const { Intents } = require("syntx.js");
Intents.Guilds; // a single intent
Intents.Fast; // Guilds, MessageContent, GuildMembers, GuildMessages; a sane default
Intents.All; // every intent there isTip
Intents.Fast is a good starting point for most bots. Reach for individual intents (or Intents.All during local testing) once you know exactly what your bot needs; Discord requires you to enable privileged intents like GuildMembers and MessageContent in the Developer Portal too.
Properties
| Property | Type | Description |
|---|---|---|
client.bot | discord.js Client | The underlying discord.js client. Use it for anything syntx.js doesn't cover directly, e.g. client.bot.user.tag. |
client.token | string | The token passed to the constructor. |
client.prefix | string | The configured text command prefix. |
client.clientId | string | The configured application ID. |
client.database / client.db | object | null | The database instance passed to the constructor, if any. |
client.commands | Map | Registered text commands, keyed by lowercase name. |
client.aliases | Map | Maps a command alias to its main command name. |
client.slashCommands | Map | Registered slash commands, keyed by command name. |
client.interactions | Map | Registered button/select menu handlers. |
client.modals | Map | Registered modal submit handlers. |
Note
client.bot is the real discord.js client, not a copy. Any discord.js method, property, or event works on it exactly as documented in the discord.js guide.
Methods
client.ready(callback)
Runs callback once, the first time the bot connects to Discord.
client.ready(() => {
console.log(`Logged in as ${client.bot.user.tag}`);
});client.start()
Logs the bot in and connects it to Discord. Call this last, after every command, event, and handler has been registered.
client.start();Warning
Unlike some other frameworks, client.start() does not return the login promise, so await client.start() will not actually wait for the connection to finish. Use client.ready() if you need to run code once the bot is online.
client.kill()
Destroys the connection to Discord. Useful for graceful shutdowns or restart scripts.
client.kill();client.setMaxListeners(max)
Raises the max listener limit on the underlying discord.js client. Helpful for larger bots that trigger Node's default "possible EventEmitter memory leak" warning.
client.setMaxListeners(20);client.command({ name, alias, content })
Registers a prefix-based text command. content receives the raw discord.js Message.
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | The command name, matched case-insensitively. |
alias | string[] | No | Alternative names that trigger the same command. |
content | function | Yes | (message) => void | Promise<void> |
client.command({
name: "ping",
alias: ["p"],
content: (message) => {
message.reply("Pong!");
},
});Warning
command() only stores the command definition in memory. You also need to call client.registerCommands() once, after defining all your commands, for the bot to actually listen for and run them. It also requires prefix to be set in the constructor.
client.registerCommands()
Starts listening for messages and dispatches them to the handlers registered with command(). Call it once, after all your client.command() calls.
client.registerCommands();Note
Errors thrown inside a command's content function are caught automatically and logged as a SyntxError; they will never crash your process.
client.slash({ data, execute, autocomplete })
Registers a slash command definition.
| Field | Type | Required | Description |
|---|---|---|---|
data | SlashCommand | Yes | The command definition, built with new SlashCommand({...}). |
execute | function | Yes | (interaction, client) => void | Promise<void> |
autocomplete | function | No | (interaction, client) => void | Promise<void>, called for autocomplete-enabled options. |
const { SlashCommand } = require("syntx.js");
client.slash({
data: new SlashCommand({
name: "ping",
description: "Replies with the bot latency",
}),
execute: async (interaction) => {
await interaction.reply(`Pong! ${client.bot.ws.ping}ms`);
},
});Warning
Just like command(), slash() only stores the definition. Call client.registerSlashCommands() once, after all your client.slash() calls, to actually publish the commands to Discord.
client.registerSlashCommands({ guildId, showLoad })
Publishes every command registered with slash() to Discord and starts listening for interactions.
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
guildId | string | No* | null | Required if any registered command has scope: "guild". |
showLoad | boolean | No | false | Prints a small colored summary of the registration to the console. |
await client.registerSlashCommands({ guildId: "123456789012345678", showLoad: true });Note
Errors thrown inside execute or autocomplete are caught automatically. If execute throws, syntx.js replies with an ephemeral "An error occurred while running this command." message instead of letting the interaction hang.
Tip
Keep commands you're actively building under scope: "guild" for instant updates, then switch to scope: "global" (the default) once they're ready; global commands can take up to an hour to show up everywhere.
client.interaction({ id, content, separator })
Registers a handler for a button or select menu whose customId matches id. id supports {placeholder} segments for dynamic values, similar to a route pattern.
client.interaction({
id: "delete-{messageId}",
content: (interaction, { messageId }) => {
interaction.reply(`Deleting message ${messageId}...`);
},
});| Field | Type | Required | Default | Description |
|---|---|---|---|---|
id | string | Yes | — | The customId pattern to match, e.g. "delete-{messageId}". |
content | function | Yes | — | (interaction, dynamicValues, separator) => void | Promise<void> |
separator | string | No | "-" | The character used to split dynamic segments in id. |
Warning
client.registerInteractions() must be called for these handlers to actually fire.
client.modal({ id, run })
Registers a handler for a modal submission. Works like interaction(), but id always uses - as its separator.
client.modal({
id: "feedback-{userId}",
run: (interaction, { userId }) => {
const text = interaction.fields.getTextInputValue("message");
interaction.reply(`Thanks <@${userId}>, got: ${text}`);
},
});Warning
Just like interaction(), this requires client.registerInteractions() to be called.
client.registerInteractions()
Attaches a single listener that dispatches button presses and select menu interactions to your interaction() handlers, and modal submissions to your modal() handlers, based on customId.
client.registerInteractions();Tip
Call this once, alongside registerCommands() and registerSlashCommands(), after every interaction()/modal() has been declared.
client.event(name, callback)
Listens to a discord.js event. name must match one of discord.js's Events values (for example "messageCreate", "guildMemberAdd", "interactionCreate").
client.event("messageCreate", (message) => {
if (message.content === "ping") message.reply("pong");
});Note
Errors thrown inside the callback are caught automatically and logged as a SyntxError instead of crashing the bot.
client.presence({ time, activities, status })
Rotates the bot's status on an interval, starting once the bot is ready.
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
time | number | Yes | — | Seconds between each rotation. |
activities | { type, content }[] | Yes | — | At least one activity to cycle through. |
status | "online" | "idle" | "dnd" | "invisible" | No | "online" | The bot's online status. |
type accepts "playing", "streaming", "listening", "watching", or "competing".
client.presence({
time: 15,
status: "idle",
activities: [
{ type: "watching", content: "the server" },
{ type: "listening", content: "/help" },
],
});client.handler(paths, showLoad)
Auto-loads and registers files from folders you point it to; a faster alternative to calling command(), event(), slash(), modal(), and interaction() by hand for every file. Every .js file inside the given folder (including subfolders) is loaded and registered automatically.
client.handler(
{
commands: "./src/commands",
events: "./src/events",
slash: "./src/slash",
interactions: "./src/interactions",
modals: "./src/modals",
},
true,
);The second argument (showLoad) is a plain boolean, not part of the first object; pass true to print a per-file load report to the console.
Each folder expects its files to export a specific shape:
| Folder | Each file exports | Registered via |
|---|---|---|
commands | { name, alias?, content } | command() |
events | { event, content } | event(event, content) |
slash | { data, execute, autocomplete? } | slash() |
interactions | { content, separator?, id?/name? } | interaction() |
modals | { run, id?/name? } | modal() |
Note
For interactions and modals, the handler's id falls back to the file name (without .js) if the file doesn't export an id or name field. For example, interactions/delete-message.js becomes id: "delete-message" automatically.
Warning
handler() only loads and registers your definitions, exactly like calling command()/slash()/etc. manually would. You still need to call registerCommands(), registerSlashCommands(), and/or registerInteractions() afterward to actually activate them.
Tip
See Using the Handler for a full worked example of every folder type, including exactly which arguments each exported function receives.
Putting it all together
const { ERXClient, Intents } = require("syntx.js");
const client = new ERXClient({
token: process.env.TOKEN,
intents: Intents.Fast,
prefix: "!",
clientId: "YOUR_APPLICATION_ID",
});
client.ready(() => {
console.log(`Logged in as ${client.bot.user.tag}`);
});
client.command({
name: "ping",
content: (message) => message.reply("Pong!"),
});
client.registerCommands();
client.start();See SlashCommand to build slash commands, Database to store bot data, Embed to send rich embeds, and Poll to attach native polls to a message. For interactive UI, see Buttons, SelectMenus, Modal, and Display.