How to manage database connections in a Go Discord bot without global vars?

I’m building a Discord bot in Go that needs to query a PostgreSQL database when users send commands. My main issue is with the discord.AddHandler(newMessage) function, since it only accepts specific arguments, leaving me unable to directly pass the database connection.

Here’s an example of my current setup:

func setupBot() {
    db := initDatabase()
    defer db.Close()
    
    bot := createDiscordBot()
    bot.AddHandler(handleMessage)
    bot.Run()
}

func handleMessage(s *discordgo.Session, m *discordgo.MessageCreate) {
    // Attempting to use the database connection here without globals
    // How can I access the connection effectively?
}

I want to avoid using global variables or reinitializing the database within the handler. Has anyone worked around this limitation to allow access to the database connection within a restricted handler signature? Any insights would be great!

I’ve faced a similar challenge in my Go projects. One solution that worked well for me was using a context-based approach. You can create a custom context that carries your database connection and pass it down to your handler functions.

Here’s how I implemented it:

type botContext struct {
    db *sql.DB
}

func withDB(db *sql.DB) func(handler func(context.Context, *discordgo.Session, *discordgo.MessageCreate)) func(*discordgo.Session, *discordgo.MessageCreate) {
    return func(handler func(context.Context, *discordgo.Session, *discordgo.MessageCreate)) func(*discordgo.Session, *discordgo.MessageCreate) {
        return func(s *discordgo.Session, m *discordgo.MessageCreate) {
            ctx := context.WithValue(context.Background(), botContext{}, db)
            handler(ctx, s, m)
        }
    }
}

func setupBot() {
    db := initDatabase()
    defer db.Close()
    
    bot := createDiscordBot()
    bot.AddHandler(withDB(db)(handleMessage))
    bot.Run()
}

func handleMessage(ctx context.Context, s *discordgo.Session, m *discordgo.MessageCreate) {
    db := ctx.Value(botContext{}).(*sql.DB)
    // Use db here
}

This approach keeps your code clean and allows for easy testing and mocking of dependencies. It’s been quite reliable in my experience.

hey, try using dependency injection. create a struct with ur db connection and discord session, then make a method for handling messages. pass this struct to ur bot setup. this way u can access the db in ur handler without globals. it’s pretty neat and keeps things organized!

One effective approach to manage database connections in your Go Discord bot without global variables is to use a custom handler struct. You can create a struct that holds both the Discord session and the database connection, then implement the handler method on this struct.

Here’s how you might restructure your code:

type BotHandler struct {
    db *sql.DB
    session *discordgo.Session
}

func (h *BotHandler) handleMessage(s *discordgo.Session, m *discordgo.MessageCreate) {
    // Use h.db to access the database connection
}

func setupBot() {
    db := initDatabase()
    defer db.Close()
    
    bot := createDiscordBot()
    handler := &BotHandler{db: db, session: bot}
    bot.AddHandler(handler.handleMessage)
    bot.Run()
}

This approach encapsulates the database connection within the handler struct, allowing you to access it in your message handling logic without relying on global variables. It’s a clean and maintainable solution that adheres to Go’s idiomatic practices.