Skip to main content

Project Setup

The Quick Start puts everything in a single file, which is fine for learning but not how you'd structure a real bot. This page covers the key integration points FlowCord needs — how you organize the rest of your project is up to you.

note

If you already have an established bot structure or are using a bot framework, skip to the sections that apply to you. FlowCord doesn't impose any particular file layout.

The three integration points​

Regardless of how your bot is organized, FlowCord needs three things wired up:

  1. A FlowCord instance — created once, shared wherever menus are registered or interactions are handled
  2. Menu registration — each menu factory registered before any interactions arrive
  3. Slash command routing — slash commands routed to handleInteraction
// 1. Create the instance once
const flowcord = new FlowCord({ client });

// 2. Register menus before the bot goes online
flowcord.registerMenu('weather', (session) => new MenuBuilder(session, 'weather')/* ... */.build());

// 3. Route slash commands
client.on('interactionCreate', async (interaction) => {
if (interaction.isChatInputCommand()) {
if (interaction.commandName === 'weather') {
await flowcord.handleInteraction(interaction, 'weather');
}
}
// Component interactions (buttons, selects) are handled automatically
// by the active session's collector — no explicit routing needed.
});

How you split these across files — whether you co-locate menu factories with slash command handlers, keep them separate, or use a command framework — is your call.

Slash command registration​

Registering commands with Discord is a separate, one-time operation — not part of your bot's startup. Discord caches registered commands globally, so you only need to re-run registration when your command definitions change.

register-commands.ts
import { REST, Routes, SlashCommandBuilder } from 'discord.js';

const commands = [
new SlashCommandBuilder()
.setName('weather')
.setDescription('Check the weather in Cerulean City'),
].map((cmd) => cmd.toJSON());

const rest = new REST().setToken(process.env.DISCORD_BOT_TOKEN!);

rest
.put(Routes.applicationCommands(process.env.APP_ID!), { body: commands })
.then(() => console.log(`Registered ${commands.length} commands`))
.catch(console.error);

Run this once with npx tsx register-commands.ts. The commands will appear in Discord within a few seconds.

Environment variables​

Your bot needs at minimum:

VariableDescription
DISCORD_BOT_TOKENYour bot's token from the Discord Developer Portal
APP_IDYour application's ID (needed for command registration)
warning

Never commit bot tokens to version control. Add .env to your .gitignore.

FlowCord configuration​

Beyond the required client, the FlowCord constructor accepts:

const flowcord = new FlowCord({
client,

// How long (ms) a session stays open without interaction. Default: 120000 (2 min)
timeout: 60_000,

// Custom error handler. Default: replies with an ephemeral error message.
onError: async (session, error) => {
console.error(`Session ${session.id} failed:`, error);
},

// Log every menu transition to the console. Useful during development.
enableTracing: process.env.NODE_ENV === 'development',
});

Next steps​

With FlowCord integrated into your bot, start with Menus & Sessions to build a solid mental model before diving into more advanced features.