Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Go using mux Router - How to pass my DB to my handlers

Tags:

go

router

mux

At the moment, I try to create a small Web-Project using Go for data handling on the server.

I try to pass my database-connection to my HandlerFunc(tions) but it does not work as expected. I am pretty new to golang, so maybe I did not understand some basic principles of this lang.

My main func looks like this:

func main() {

    db, err := config.NewDB("username:password@/databasename?charset=utf8&parseTime=True")
    if err != nil {
        log.Panic(err)
    }   
    env := &config.Env{DB: db} 

    router := NewRouter(env)
    log.Fatal(http.ListenAndServe(":8080", router))
}

My Router:

func NewRouter(env *config.Env) *mux.Router {
    router := mux.NewRouter().StrictSlash(true)
    for _, route := range routes {
        var handler http.Handler

        handler = route.HandlerFunc
        handler = Logger(handler, route.Name)

        router.
            Methods(route.Method).
            Path(route.Pattern).
            Name(route.Name).
            Handler(handler)
    }   
    return router
}

and my routes:

type Route struct {
    Name        string
    Method      string
    Pattern     string
    HandlerFunc http.HandlerFunc
}

type Routes []Route

var routes = Routes{
    Route{
        "Index",
        "GET",
        "/",
        controller.Index,
    },  
    Route{
        "Show",
        "GET",
        "/todos/{todoId}",
        controller.TodoShow,
    },  
    Route{
        "Create",
        "POST",
        "/todos",
        controller.TodoCreate,
    },  
}

So - how can I pass my "env" (or env.DB) to my FuncHandlers? I tried many things, but none of them worked.

like image 802
Newbie Avatar asked Nov 11 '15 08:11

Newbie


People also ask

How does Gorilla mux work?

Package gorilla/mux implements a request router and dispatcher for matching incoming requests to their respective handler. The name mux stands for "HTTP request multiplexer". Like the standard http.

What is Gorilla mux used for?

Gorilla Mux provides functionalities for matching routes, serving static files, building single-page applications (SPAs), middleware, handling CORS requests, and testing handlers. This tutorial will walk you through using the Gorilla Mux package as a router for your applications.

What is mux Vars?

The name mux stands for "HTTP request multiplexer". Like the standard http. ServeMux, mux. Router matches incoming requests against a list of registered routes and calls a handler for the route that matches the URL or other conditions.

What are routers in Golang?

Implementation of router in Golang. In Golang, the standard package net/http has a routing function. That feature is called multiplexer in Golang. However, the standard package does not support path parameters, so if you want to use it, you need to prepare an external package or extend the standard multiplexer.


2 Answers

You have three options:

  1. Make your database connection pool a global, so that you don't have to pass it. sql.DB is safe for concurrent access, and this is the easiest approach. The downside is that it makes testing harder and obfuscates "where" the pool is coming from - e.g.

    var db *sql.DB
    
    func main() {
        var err error
        db, err = sql.Open(...)
        // Now accessible globally, no need to pass it around
        // ...
     }
    
  2. Wrap your handlers in a closure, which makes it accessible to the inner handler. You'll need to adapt this to your range-over-routes approach—which is a little obtuse IMO, and makes it harder to see what routes exist, but I digress—for example:

    func SomeHandler(db *sql.DB) http.HandlerFunc {
        fn := func(w http.ResponseWriter, r *http.Request) {
            res, err := db.GetThings()
            // etc.
        }
    
        return http.HandlerFunc(fn)
    }
    
    func main() {
        db, err := sql.Open(...)
        http.HandleFunc("/some-route", SomeHandler(db))
        // etc.
    }
    
  3. Create a custom handler type that accepts a handler - e.g.

    type AppHandler struct {
        Handler func(env *config.Env, w http.ResponseWriter, r *http.Request)
        Env *config.Env
    }
    
    // ServeHTTP allows your type to satisfy the http.Handler interface.
    func (ah *AppHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
        ah.Handler(ah.Env, w, r)
    }
    
    func SomeHandler(env *config.Env, w http.ResponseWriter, r *http.Request) {
        res, err := env.DB.GetThings()
        // etc.
    }
    

Note that (shameless plug!) I've written about the last approach in detail, and Alex Edwards has an excellent blog post on approaches to accessing DB pools in Go programs as well.

The only strict advice I can give is that you should shy away from passing your DB pool around in a request context, which is inefficient and not good practice (request contexts are for temporary, per-request objects).

like image 84
elithrar Avatar answered Oct 03 '22 12:10

elithrar


You can always have "env" defined as global variable.

But before everyone will hate me, this is not a good solution! You should create a package that encapsulate the access to your database with public function that state your exact intent.

Something along the lines of

Package db

var config ....

func ShowTodos(params ... ) result {
   your database access code here.... 
}

and from your router function access it with

db.ShowTodos(...)
like image 22
Eden Avatar answered Oct 03 '22 12:10

Eden