Gin: How to Pass db to router functions

Created on 3 Jun 2017  路  5Comments  路  Source: gin-gonic/gin

I'm opening the sqlite database in the main function

db, err := sql.Open("sqlite3", "./libreread.db")
CheckError(err)
defer db.Close()

And I have these router handlers

r.GET("/", GetHomePage)
r.GET("/signin", GetSignIn)
r.POST("/signin", PostSignIn)
...

How to pass that db value through the router handler func PostSignin(c *gin.Context) ?

So that I could avoid opening and closing the database each time in the functions.

Thanks!

question

Most helpful comment

@mysticmode you can put db into a custom Env type:

type Env struct {
    db *sql.DB
}

func (e *Env) GetHomePage(c *gin.Context) {
    //e.db.Query("SELECT * FROM USER")
    //omitted..
}
func (e *Env) GetSignIn(c *gin.Context) {
    //omitted...
}

func (e *Env) PostSignIn(c *gin.Context) {
    //omitted...
}

func main() {
    db, _ := sql.Open("sqlite3", "./libreread.db")

    r := gin.New()

    env := &Env{db: db}
    r.GET("/", env.GetHomePage)
    r.GET("/signin", env.GetSignIn)
    r.POST("/signin", env.PostSignIn)
}

All 5 comments

I usually create a middleware that sets a context value.

func Site(activeSite *sites.Site) gin.HandlerFunc {
    return func(c *gin.Context) {

        c.Set(constants.CONTEXT_SITE, activeSite)
        c.Next()
    }
}

```go

r.Use(middleware.Site(site))
And then inside your handler:
```go
    site := c.MustGet(constants.CONTEXT_SITE).(*sites.Site)

Same concept applies to database objects :)

@mysticmode you can put db into a custom Env type:

type Env struct {
    db *sql.DB
}

func (e *Env) GetHomePage(c *gin.Context) {
    //e.db.Query("SELECT * FROM USER")
    //omitted..
}
func (e *Env) GetSignIn(c *gin.Context) {
    //omitted...
}

func (e *Env) PostSignIn(c *gin.Context) {
    //omitted...
}

func main() {
    db, _ := sql.Open("sqlite3", "./libreread.db")

    r := gin.New()

    env := &Env{db: db}
    r.GET("/", env.GetHomePage)
    r.GET("/signin", env.GetSignIn)
    r.POST("/signin", env.PostSignIn)
}

@mysticmode you can put db into a custom Env type:

type Env struct {
  db *sql.DB
}

func (e *Env) GetHomePage(c *gin.Context) {
  //e.db.Query("SELECT * FROM USER")
  //omitted..
}
func (e *Env) GetSignIn(c *gin.Context) {
  //omitted...
}

func (e *Env) PostSignIn(c *gin.Context) {
  //omitted...
}

func main() {
  db, _ := sql.Open("sqlite3", "./libreread.db")

  r := gin.New()

  env := &Env{db: db}
  r.GET("/", env.GetHomePage)
  r.GET("/signin", env.GetSignIn)
  r.POST("/signin", env.PostSignIn)
}

would this not effect performance since the environment receiver will be needed for every request which essentially makes it synchronous or at the very least is vulnerable to a data race?

Just open db connection as global concurrency variable and get access to it from any function, goroutine etc.

```go
// DB is safe to be accessed from multiple goroutines
// https://golang.org/pkg/database/sql/#DB
var DBPointer, _ = sql.Open("sqlite3", "./libreread.db")

func DBTestHandler(c *gin.Context) {
rows, err := DBPointer.Query("select * from Products")
if err != nil {
panic(err)
}
defer rows.Close()
}

func main() {
r := gin.New()
r.GET("/test", DBTestHandler)
srv := &http.Server{
Handler: r,
Addr: "127.0.0.1:5050",
WriteTimeout: 15 * time.Second,
ReadTimeout: 15 * time.Second,
}
log.Fatal(srv.ListenAndServe())
}```

@McGiver- No, this is totally fine. The database object is safe to use asynchronously. And also, you should treat the struct as a holder object only / something to group your endpoints in, and not as an active data source for operations within your handler functions if that makes any sense.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

wangcn picture wangcn  路  3Comments

gplume picture gplume  路  3Comments

ccaza picture ccaza  路  3Comments

frederikhors picture frederikhors  路  3Comments

ghost picture ghost  路  3Comments