Skip to main content
CommandKit provides lifecycle hooks that allow you to run code at specific points during your application’s initialization and operation. These hooks are essential for setting up integrations, initializing services, and performing cleanup operations.

Available Hooks

onBootstrap

Runs when the CommandKit instance is created, before most dependencies are initialized.
import { onBootstrap } from 'commandkit';

onBootstrap(async (commandkit) => {
  console.log('CommandKit instance created');
  // Basic initialization logic here
});
Source: packages/commandkit/src/commandkit.ts:71

Use Cases

  • Register early plugins
  • Set up global error handlers
  • Initialize logging systems
  • Configure environment variables

Characteristics

  • ✅ CommandKit instance available
  • ❌ Client not yet available
  • ❌ Commands/events not loaded
  • ❌ Plugins not fully initialized

onApplicationBootstrap

Runs after the CommandKit instance is fully initialized with all dependencies available.
import { onApplicationBootstrap } from 'commandkit';

onApplicationBootstrap(async (commandkit) => {
  console.log('Application fully initialized');
  console.log('Client ready:', commandkit.client.isReady());
  console.log('Commands loaded:', commandkit.commandHandler);
});
Source: packages/commandkit/src/commandkit.ts:87

Use Cases

  • Access Discord client
  • Query loaded commands/events
  • Set up database connections
  • Initialize third-party services
  • Register dynamic commands

Characteristics

  • ✅ CommandKit instance available
  • ✅ Client logged in and ready
  • ✅ Commands/events loaded
  • ✅ Plugins fully initialized
  • ✅ All handlers registered

Hook Registration

Type Signatures

type BootstrapFunction = 
  | ((commandkit: CommandKit) => void)
  | ((commandkit: CommandKit) => Promise<void>);

function onBootstrap(fn: BootstrapFunction): void;
function onApplicationBootstrap(fn: BootstrapFunction): void;
Source: packages/commandkit/src/commandkit.ts:48-52

Multiple Hooks

You can register multiple hooks of the same type. They execute sequentially in registration order.
onBootstrap(async (kit) => {
  console.log('Hook 1');
});

onBootstrap(async (kit) => {
  console.log('Hook 2'); // Runs after Hook 1
});

Error Handling

Errors in hooks are caught and logged without stopping the application.
onBootstrap(async (kit) => {
  throw new Error('This error will be logged');
  // Application continues
});

onBootstrap(async (kit) => {
  console.log('This still runs');
});
Source: packages/commandkit/src/commandkit.ts:206-219

Practical Examples

Database Initialization

import { onApplicationBootstrap } from 'commandkit';
import { initDatabase } from './database';

onApplicationBootstrap(async (commandkit) => {
  console.log('Initializing database...');
  
  const db = await initDatabase();
  
  // Store for global access
  commandkit.store.set('database', db);
  
  console.log('Database ready');
});

Feature Flag Setup

import { onApplicationBootstrap } from 'commandkit';
import { setFlagProvider } from 'commandkit';
import { LaunchDarklyProvider } from './providers/launchdarkly';

onApplicationBootstrap(async (commandkit) => {
  const provider = new LaunchDarklyProvider({
    sdkKey: process.env.LAUNCHDARKLY_KEY!,
  });
  
  await provider.initialize();
  setFlagProvider(provider);
  
  console.log('Feature flags initialized');
});

Analytics Integration

import { onApplicationBootstrap } from 'commandkit';
import { Analytics } from '@segment/analytics-node';

onApplicationBootstrap(async (commandkit) => {
  const analytics = new Analytics({
    writeKey: process.env.SEGMENT_KEY!,
  });
  
  commandkit.store.set('analytics', analytics);
  
  // Track application start
  analytics.track({
    userId: commandkit.client.user.id,
    event: 'Bot Started',
    properties: {
      guildCount: commandkit.client.guilds.cache.size,
    },
  });
});

Dynamic Command Registration

import { onApplicationBootstrap } from 'commandkit';

onApplicationBootstrap(async (commandkit) => {
  const guildId = process.env.TEST_GUILD_ID;
  
  if (guildId && commandkit.config.dev) {
    // Register test commands only in dev mode
    const testCommands = await loadTestCommands();
    
    await commandkit.client.application.commands.set(
      testCommands,
      guildId
    );
    
    console.log('Test commands registered');
  }
});

Health Check Endpoint

import { onApplicationBootstrap } from 'commandkit';
import express from 'express';

onApplicationBootstrap(async (commandkit) => {
  const app = express();
  
  app.get('/health', (req, res) => {
    res.json({
      status: 'healthy',
      uptime: process.uptime(),
      guilds: commandkit.client.guilds.cache.size,
      commands: commandkit.commandHandler.commands.size,
    });
  });
  
  app.listen(3000, () => {
    console.log('Health check endpoint listening on :3000');
  });
});

Plugin Integration

Hooks are commonly used in plugins to initialize functionality.
import { onApplicationBootstrap } from 'commandkit';

export function createCachePlugin() {
  return {
    name: 'cache-plugin',
    
    setup() {
      onApplicationBootstrap(async (commandkit) => {
        const cache = new Map();
        commandkit.store.set('cache', cache);
        
        console.log('Cache plugin initialized');
      });
    },
  };
}

Hook Execution Flow

Timeline

  1. CommandKit createdonBootstrap hooks run
  2. Dependencies initialize
  3. Client connects to Discord
  4. Commands and events load
  5. Handlers registeronApplicationBootstrap hooks run
  6. Application ready

Best Practices

Choose the Right Hook

// ❌ Wrong - Client not ready yet
onBootstrap(async (kit) => {
  console.log(kit.client.user.tag); // Error!
});

// ✅ Correct - Client is ready
onApplicationBootstrap(async (kit) => {
  console.log(kit.client.user.tag); // Works!
});

Handle Async Operations

onApplicationBootstrap(async (commandkit) => {
  // Use async/await for initialization
  const config = await fetchConfig();
  const service = await initService(config);
  
  commandkit.store.set('service', service);
});

Store Services Globally

onApplicationBootstrap(async (commandkit) => {
  const redis = await createRedisClient();
  
  // Store for access in commands
  commandkit.store.set('redis', redis);
});

// In a command
export const chatInput: ChatInputCommand = async (ctx) => {
  const redis = ctx.commandkit.store.get('redis');
  await redis.set('key', 'value');
};

Log Initialization Steps

onApplicationBootstrap(async (commandkit) => {
  console.log('[Init] Starting database connection...');
  const db = await connectDatabase();
  console.log('[Init] Database connected');
  
  console.log('[Init] Loading configuration...');
  const config = await loadConfig();
  console.log('[Init] Configuration loaded');
  
  console.log('[Init] Application ready');
});

Cleanup on Exit

onApplicationBootstrap(async (commandkit) => {
  const db = await initDatabase();
  commandkit.store.set('database', db);
  
  // Register cleanup handler
  process.on('SIGINT', async () => {
    console.log('Closing database connection...');
    await db.close();
    process.exit(0);
  });
});

TypeScript Support

Hooks are fully typed with the CommandKit instance.
import { onApplicationBootstrap, CommandKit } from 'commandkit';

onApplicationBootstrap((commandkit: CommandKit) => {
  // Full type information available
  commandkit.client.user?.tag;
  commandkit.commandHandler.commands;
  commandkit.eventHandler.events;
});

Internal Implementation

Hooks are stored in Sets and executed during application initialization.
// Internal: Hook storage
const bootstrapHooks = new Set<BootstrapFunction>();
const onApplicationBootstrapHooks = new Set<BootstrapFunction>();

// Internal: Hook execution
async #bootstrapHooks() {
  for (const hook of bootstrapHooks) {
    try {
      await hook(this);
    } catch (e) {
      Logger.error`Error while executing bootstrap hook: ${e}`;
    } finally {
      bootstrapHooks.delete(hook);
    }
  }
  
  bootstrapHooks.clear();
}
Source: packages/commandkit/src/commandkit.ts:54-55, 206-219

Debugging

Enable debug logging to see hook execution.
onBootstrap((kit) => {
  console.log('[Hook] onBootstrap running');
});

onApplicationBootstrap((kit) => {
  console.log('[Hook] onApplicationBootstrap running');
  console.log('[Hook] Client ready:', kit.client.isReady());
  console.log('[Hook] Commands:', kit.commandHandler.commands.size);
});

Build docs developers (and LLMs) love