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.

JavaScript
const { ERXClient } = require("syntx.js");

Constructor

JavaScript
new ERXClient(options);

Options

OptionTypeRequiredDescription
tokenstringYesYour bot token from the Discord Developer Portal.
intentsnumber[] | numberYesGateway intents to enable. Use the Intents helper.
prefixstringNoPrefix for text commands. Only required if you use command().
clientIdstringNoYour application/client ID. Only required if you use slash().
databaseobjectNoA SyntxDB instance or any object exposing your own database methods.
JavaScript
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.

JavaScript
const { Intents } = require("syntx.js");

Intents.Guilds; // a single intent
Intents.Fast; // Guilds, MessageContent, GuildMembers, GuildMessages; a sane default
Intents.All; // every intent there is

Tip

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

PropertyTypeDescription
client.botdiscord.js ClientThe underlying discord.js client. Use it for anything syntx.js doesn't cover directly, e.g. client.bot.user.tag.
client.tokenstringThe token passed to the constructor.
client.prefixstringThe configured text command prefix.
client.clientIdstringThe configured application ID.
client.database / client.dbobject | nullThe database instance passed to the constructor, if any.
client.commandsMapRegistered text commands, keyed by lowercase name.
client.aliasesMapMaps a command alias to its main command name.
client.slashCommandsMapRegistered slash commands, keyed by command name.
client.interactionsMapRegistered button/select menu handlers.
client.modalsMapRegistered 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.

JavaScript
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.

JavaScript
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.

JavaScript
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.

JavaScript
client.setMaxListeners(20);

client.command({ name, alias, content })

Registers a prefix-based text command. content receives the raw discord.js Message.

FieldTypeRequiredDescription
namestringYesThe command name, matched case-insensitively.
aliasstring[]NoAlternative names that trigger the same command.
contentfunctionYes(message) => void | Promise<void>
JavaScript
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.

JavaScript
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.

FieldTypeRequiredDescription
dataSlashCommandYesThe command definition, built with new SlashCommand({...}).
executefunctionYes(interaction, client) => void | Promise<void>
autocompletefunctionNo(interaction, client) => void | Promise<void>, called for autocomplete-enabled options.
JavaScript
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.

OptionTypeRequiredDefaultDescription
guildIdstringNo*nullRequired if any registered command has scope: "guild".
showLoadbooleanNofalsePrints a small colored summary of the registration to the console.
JavaScript
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.

JavaScript
client.interaction({
  id: "delete-{messageId}",
  content: (interaction, { messageId }) => {
    interaction.reply(`Deleting message ${messageId}...`);
  },
});
FieldTypeRequiredDefaultDescription
idstringYesThe customId pattern to match, e.g. "delete-{messageId}".
contentfunctionYes(interaction, dynamicValues, separator) => void | Promise<void>
separatorstringNo"-"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.

JavaScript
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.

JavaScript
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").

JavaScript
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.

OptionTypeRequiredDefaultDescription
timenumberYesSeconds between each rotation.
activities{ type, content }[]YesAt 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".

JavaScript
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.

JavaScript
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:

FolderEach file exportsRegistered 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

JavaScript
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.