Recipes

Database Integration

Seyfert is database-agnostic — you can attach any database client (Prisma, Drizzle, Mongoose, etc.) to your bot. Here are the two recommended patterns.

Pattern 1: Module Augmentation

The simplest approach. Attach the database client to the Seyfert client instance and declare its type:

import { , type  } from 'seyfert';

// Replace with your actual database client (Prisma, Drizzle, etc.)
declare class  {
    (): <void>;
    (: string): <{ : string; : number } | null>;
}

const  = new () as  & { :  };

. = new ();
await ..();

await .();
await .();

declare module 'seyfert' {
    interface UsingClient extends <<true>> {}

    interface  {
        : ;
    }
}

Pattern 2: Extended Client Class

A more structured approach using class inheritance:

class  extends <true> {
     = new ();

    async () {
        await this..();

        this.({
            // middlewares, cache, etc.
        });

        await this.();
        await this.();
    }
}

const  = new ();
await .();

declare module 'seyfert' {
    interface UsingClient extends <> {}
}

Usage in Commands

Once the database client is attached, access it from any command via ctx.client.db:

import { Command, Declare, type CommandContext } from 'seyfert';

@Declare({
    name: 'balance',
    description: 'Check your balance',
})
export default class BalanceCommand extends Command {
    async run(ctx: CommandContext) {
        const user = await ctx.client.db.findUser(ctx.author.id);

        if (!user) {
            return ctx.write({ content: 'You have no account yet.' });
        }

        await ctx.write({
            content: `Your balance: **${user.balance}** coins`,
        });
    }
}

Usage in Events

The same pattern works in events, since the client is always available:

import { createEvent } from 'seyfert';

export default createEvent({
    data: { name: 'guildMemberAdd' },
    async run(member, client) {
        await client.db.createUser(member.id, {
            guildId: member.guildId,
            joinedAt: new Date(),
        });
    },
});

Usage in Middlewares

import { createMiddleware } from 'seyfert';

interface UserData {
    id: string;
    isPremium: boolean;
}

export const withUser = createMiddleware<UserData>(async ({ context, next, stop }) => {
    const user = await context.client.db.findUser(context.author.id);

    if (!user) {
        return stop('You need to create an account first. Use /register.');
    }

    next(user);
});

Then access the data in your command with type safety:

import { Command, Declare, Middlewares, type CommandContext } from 'seyfert';

@Declare({
    name: 'premium',
    description: 'Check premium status',
})
@Middlewares(['withUser'])
export default class PremiumCommand extends Command {
    async run(ctx: CommandContext<never, 'withUser'>) {
        const user = ctx.metadata.withUser;
        await ctx.write({
            content: user.isPremium ? 'You are a premium user!' : 'You are not premium.',
        });
    }
}