I\'ve read a few StackOverflow answers on how we handling the db connection. Since it\'s a pool, we can define it globally and use it in multiple goroutines and it\'s safe.
Simple answer: pass an initialised connection pool to your packages' own globals.
e.g.
// package stuff
var DB *sql.DB
func GetAllStuff() (*Stuff, error) {
err := DB.Query("...")
// etc.
}
// package things
var DB *sql.DB
func GetAllThings() (*Thing, error) {
err := DB.Query("...")
// etc.
}
// package main
func main() {
db, err := sql.Open("...")
if err != nil {
log.Fatal(err)
}
stuff.DB = db
things.DB = db
// etc.
}
We define package-level globals, make sure they're exported (capitalised) and then pass a pointer to our connection pool to them.
This is "okay", but can mask "where" things are being used. If you're looking at a handler it may not be clear where the connection is coming from, especially as your package grows. A more scalable approach might look like the below:
// package stuff
type DB struct {
*sql.DB
}
func New(db *sql.DB) (*DB, error) {
// Configure any package-level settings
return &DB{db}, nil
}
func (db *DB) GetAllStuff() (*Stuff, error) {
err := db.Query("...")
// etc.
}
// package things
type DB struct {
*sql.DB
}
func New(db *sql.DB) (*DB, error) {
// Configure any package-level settings
return &DB{db}, nil
}
func (db *DB) GetAllThings() (*Thing, error) {
err := db.Query("...")
// etc.
}
// package main
func main() {
db, err := sql.Open("...")
if err != nil {
log.Fatal(err)
}
stuffDB, err := stuff.New(db)
if err != nil {
log.Fatal(err)
}
thingsDB, err := things.New(db)
if err != nil {
log.Fatal(err)
}
// Simplified.
http.HandleFunc("/stuff/all", stuff.ShowStuffHandler(stuffDB))
http.HandleFunc("/things/all", things.ShowThingsHandler(thingsDB))
// etc.
}
func ShowStuffHandler(db *stuff.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// We can use our stuff.DB here
stuff, err := db.GetAllStuff()
// etc.
}
}
If you have more than just the DB connection as a dependency (e.g. config params, hostnames, etc.) , wrap them in an things.Env
struct or a stuff.Env
struct for each package.
An example would be to have a things.New("deps...") *Env
function that returns a configured *things.Env
that contains the dependencies used by your things
package.
There is also the option of creating another package to hold your database connection-related settings. It can then have a package level global, which can be initialized in main
and used in any package that is importing it.
This way, you can explicitly see that the database package is being imported. Here is some sample code.
package database
var (
// DBCon is the connection handle
// for the database
DBCon *sql.DB
)
package main
import "myApp/database"
func main() {
var err error
database.DBCon, err = sql.Open("postgres", "user=myname dbname=dbname sslmode=disable")
}
package user
import "myApp/database"
func Index() {
// database handle is available here
database.DBCon
...
}