77

I am trying to pass my database object along to my handlers, instead of having a global object. But I don't know if this is possible, I'm using Gorilla Mux package, and I can see that it takes a closure as a second param.

// https://github.com/gorilla/mux/blob/master/mux.go#L174
// HandleFunc registers a new route with a matcher for the URL path.
// See Route.Path() and Route.HandlerFunc().
func (r *Router) HandleFunc(path string, f func(http.ResponseWriter,
    *http.Request)) *Route {
    return r.NewRoute().Path(path).HandlerFunc(f)
}

Which then defines the params i can use, ideally i would like to have a third param like this.

// In my main
router.HandleFunc("/users/{id}", showUserHandler).Methods("GET")

func showUserHandler(w http.ResponseWriter, r *http.Request, db *gorm.DB) {
    fmt.Fprintf(w, "We should fetch the user with id %s", vars["id"])
}

Is there a workaround? Or do I need a global db object? I am new to Go, so please explain a potential answer in detail.

Philip Kirkbride
  • 21,381
  • 38
  • 125
  • 225
MartinElvar
  • 5,695
  • 6
  • 39
  • 56
  • 2
    http://stackoverflow.com/a/26106655/6309 can give you some ideas too. – VonC Oct 06 '14 at 08:12
  • @VonC I actually tried http://simonsdotnet.wordpress.com/2014/09/06/extending-gos-http-handlers/ but found that it did't work with Gorilla, as the http package just wants the ServeHTTP, but the Gorilla wants a func with specific params. I could be wrong ofc. – MartinElvar Oct 06 '14 at 08:18

1 Answers1

136

Welcome to Go.

It is acceptable to have global variables and specially database objects.

However, there are few ways to workaround that if you prefer not to, for example you can create a struct and define your showHandler on it.

type Users struct {
    db *gorm.DB
}

func (users *Users) showHandler(w http.ResponseWriter, r *http.Request) {
    //now you can use users.db
}
func (users *Users) addHandler(w http.ResponseWriter, r *http.Request) {
    //now you can use users.db
}

// setup
users := &Users{db: createDB()}
router.HandleFunc("/users/{id}", users.showHandler).Methods("GET")
router.HandleFunc("/users/new", users.addHandler)
//etc

Another approach is creating a wrapper function:

db := createDB()
router.HandleFunc("/users/{id}", func(w http.ResponseWriter, r *http.Request) {
    showUserHandler(w, r, db)
}).Method("GET")
OneOfOne
  • 95,033
  • 20
  • 184
  • 185
  • Thank you OneOfOne. The idea was, that i wanted a more DI way of doing it, maybe i'm overthinking it. Which solution would you say is the cleanest? – MartinElvar Oct 06 '14 at 08:13
  • @MartinElvar for a database connection, I'd say a global variable is fine. if you want specific variables that can only be used by a specific set of functions then the struct solution. – OneOfOne Oct 06 '14 at 08:15
  • 1
    @MartinElvar also keep in mind that while most database handlers are safe for concurrency, everything else isn't, so if you set a variable in one handlers and get it in another, you will have to use some sort of locking. – OneOfOne Oct 06 '14 at 08:16
  • 7
    I'd go with the "wrap" method proposed. I've seen it commonly used to wrap the handler with "middleware" services. – Sridhar Jan 27 '17 at 13:07
  • @OneOfOne How can we use closure like showHandler taking a user parameter and return func (w responseHandler, req r*http.Request) {} – kailash yogeshwar Aug 26 '19 at 12:52
  • I know it's some years ago, but I still some go programmers saying global is fine. To me, global is not fine. Pass your collaborators. Otherwise testing is a nightmare. (Yes you can do end2end but it takes ages) – dierre Dec 08 '20 at 13:36