Go with Mux Router - How to pass my DB to my handlers

I'm currently trying to create a small web project using Go to process data on a server.

I am trying to pass my database connection to my HandlerFunc (), but it is not working properly. I am new to the golang, so perhaps I did not understand some of the basic principles of this language.

My main function is as follows:

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 a lot, but none of them worked.

+5
source share
2 answers

You have three options:

  • Make your database connection pool global so you don’t need to transfer it. sql.DB is safe for simultaneous access, and this is the easiest approach. The downside is that it makes testing more difficult and confuses the "where" pool happens - for example,

     var db *sql.DB func main() { var err error db, err = sql.Open(...) // Now accessible globally, no need to pass it around // ... } 
  • Wrap your handlers in a closure to make it accessible to the internal handler. You will need to adapt this to your approach with a range of routes - this is a bit dumb IMO and makes it harder to view routes, but I'm distracted, 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. } 
  • Create your own type of handler that the handler accepts - for example,

     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. } 

Please note that (shameless plugin!) I wrote about the latter approach in detail , and Alex Edwards has an excellent blog post on approaches to accessing database pools in Go programs.

The only strict advice I can give is that you should avoid transferring your DB pool in the context of the query, which is inefficient and impractical (query contexts are for temporary objects for each query).

+11
source

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

But before everyone hates me, this is not a good solution! You must create a package that encapsulates access to your database using a public function that indicates your exact intention.

Something along the lines

 Package db var config .... func ShowTodos(params ... ) result { your database access code here.... } 

and from your router function, access it using

 db.ShowTodos(...) 
0
source

Source: https://habr.com/ru/post/1235674/


All Articles