Database & Repositories

Disapp Core utilizes a Repository Pattern to eradicate the messy boilerplate typically found in standard database drivers, introducing an asynchronous memory layer to your bot. Thanks to its native structures, generating tables or data architectures takes merely seconds.

Spinning Up the Database

To synchronize tables or open your ongoing connections immediately when your Discord bot executes, you must invoke the initializeDatabase method stemming from the core library.

import { initializeDatabase } from "@disapp/core";

async function startBot() {
  await initializeDatabase();
  await client.start();
}

startBot();

Native Repository Classes

As part of the Core package layout, the 4 most prominently needed data tables in the Discord ecosystem are presented to you fully configured (equipped with ORM mappings):

UserRepository

Manages individualized user data streams. Generally utilized for resolving credits, experience points (XP), and comprehensive profile statuses.

GuildRepository

Preserves server-wide parameters (Guild) inherently supporting custom prefix logs, module scopes, and configuration maps.

VoiceActivityRepository

Documents analytical session timestamps revealing exactly how long users persist inside of vocal channels.

UserStatsRepository

Integrates metrics such as Level scaling systems and compounded message rates effortlessly into a permanent layer.

Creating or Reading Entries

You can invoke any native repository directly into your command scope, issuing immediate CRUD (Create, Read, Update, Delete) directives seamlessly.

import { UserRepository } from '@disapp/core';

async execute(interaction: any) {
  const userRepo = new UserRepository();

  let userData = await userRepo.getOrCreate(
    BigInt(interaction.user.id),
    interaction.user.username,
    interaction.user.avatar || undefined
  );

  userData.credits += 50;
  await userRepo.update(userData);

  await interaction.reply(`Your revised balance is: ${userData.credits}`);
}

Repository Methods

UserRepository

const userRepo = new UserRepository();

await userRepo.getOrCreate(userId: bigint, username: string, avatar?: string)
await userRepo.findById(userId: bigint)
await userRepo.update(user: User)
await userRepo.delete(userId: bigint)

GuildRepository

const guildRepo = new GuildRepository();

await guildRepo.getOrCreate(guildId: bigint, name: string)
await guildRepo.findById(guildId: bigint)
await guildRepo.update(guild: Guild)
await guildRepo.delete(guildId: bigint)

UserStatsRepository

const statsRepo = new UserStatsRepository();

await statsRepo.getOrCreate(userId: bigint, guildId: bigint)
await statsRepo.addXP(userId: bigint, guildId: bigint, amount: number)
await statsRepo.getTopPlayers(guildId: bigint, limit: number)

VoiceActivityRepository

const voiceRepo = new VoiceActivityRepository();

await voiceRepo.startSession(userId: bigint, guildId: bigint, channelId: bigint)
await voiceRepo.endSession(userId: bigint, guildId: bigint)
await voiceRepo.getWeeklyStats(guildId: bigint)

Defense via RequireDatabase Middleware

If operations strictly hinge on database availability, you can secure and protect your endpoints by executing the native middleware to prevent crashes upon database dropout faults.

import { Command, RequireDatabase } from "@disapp/core";

export default class PayCommand extends Command {
  constructor() {
    super({
      name: "pay",
      description: "Pay another user",
      data: new SlashCommandBuilder()
        .setName("pay")
        .setDescription("Pay another user"),
      middlewares: [RequireDatabase()],
      execute: async () => {},
    });
  }
}

Environment Configuration

Ensure your .env file contains the database URL:

DATABASE_URL=postgresql://user:password@localhost:5432/database

Database Migration

Run migrations to create tables:

pnpm db:migrate

Best Practices

  • Always use getOrCreate - Ensures user/guild exists before operations
  • Use BigInt for IDs - Discord IDs are too large for regular numbers
  • Add RequireDatabase middleware - Prevents crashes when database is unavailable
  • Handle errors gracefully - Wrap database operations in try-catch blocks

Learn More