I've decided to try breaking my project into a MVC type view, so to start I wanted to put all my routing into a controller folder and I put my database connection into another folder titled db.
I can't figure out how to make the database connection work smoothly. Back when all of the files were in my package main I just called InitDb() in main and in all of my other files in the main package I had access to the db variable. Now that I made db it's down package and imported it, nothing is recognized.
I also don't know where to call InitDb() and defer db.Close() anymore since it's not all in main.
db/db.go
package database
import (
"fmt"
"database/sql"
)
var db *sql.DB
const (
dbhost = "localhost"
dbuser = "root"
dbpass = "password"
dbname = "user"
)
func InitDb() {
var err error
connectionString := fmt.Sprintf("%s:%s#/%s", dbuser, dbpass, dbname)
db, err = sql.Open("mysql", connectionString)
if err != nil {
panic(err)
}
err = db.Ping()
if err != nil {
panic(err)
}
fmt.Println("Successfully connected!")
}
controllers/index.go
package controllers
import (
"net/http"
"fmt"
"db"
"github.com/gorilla/mux"
)
func TestHandler(r *mux.Router)
r.HandleFunc("/index", test).Methods("GET")
}
func test(w http.ResponseWriter, r *http.Request) {
// database undefined
err := database.QueryRow("some sql statement")
CheckErr(err)
}
main.go
package main
import (
"net/http"
_ "github.com/go-sql-driver/mysql"
"github.com/gorilla/mux"
)
func main() {
r := mux.NewRouter()
controllers.TestHandler(r)
log.Fatal(http.ListenAndServe("localhost:8000", r))
}
While not required it is a good idea to have the package name be the same as the folder in which it lives, so either do:
db/db.go
package db
// ...
Or do:
database/db.go
package database
// ...
I would not recommend mixing the two.
You can have your database package export a Close function and have main call it when it's done.
database/db.go
package database
import (
"fmt"
"database/sql"
)
var db *sql.DB
func Close() {
db.Close()
}
// ...
main.go
package main
import "database"
func main() {
defer database.Close()
// ...
}
Or just don't close it in this case. When main exits the *sql.DB does not stay alive outside your program, it will not take up connection slots if the program is not running. Closing makes sense only if you're using multiple instances of *sql.DB and there is danger that they will start blocking while waiting for a connection. If you have only one that's shared by the whole program then you should be ok not calling defer close.
Related
I'm creating small web app and I want to organize interaction between DB in main package and handlers which located in other package ("handlers"). And i want to find out can i set interaction between handlers in another package and db or it is bad practice to place handler not in the main package?
package main
import (
"handlers"
"net/http"
)
func main() {
//...
DB, err = ConnectToPostgres()
//...
http.HandleFunc("/adduser/", handlers.AddUser)
http.HandleFunc("/getinfo/", handlers.GetUserInfo)
http.ListenAndServe(":8080", nil)
}
I always use this code
package providers
import (
"fmt"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/postgres"
"log"
)
var SQL *gorm.DB
type Postgres struct{}
func (s Postgres) Connect() *gorm.DB {
dsn := s.getDSN()
instance, err := gorm.Open("postgres", dsn)
if err != nil {
log.Panicf("Postgres Error: %+v", err)
}
return instance
}
func (s *Postgres) getDSN() string {
c := Config.Storage.Postgres
dsn := fmt.Sprintf("user=%s", c.User)
if c.Pass != "" {
dsn += fmt.Sprintf(" password=%s", c.Pass)
}
dsn += fmt.Sprintf(" host=%s", c.Host)
dsn += fmt.Sprintf(" dbname=%s", c.Name)
if !c.SSL {
dsn += " sslmode=disable"
}
return dsn
}
func init() {
conn := Postgres{}
SQL = conn.Connect()
SQL.LogMode(false)
if Config.App.Debug {
SQL.LogMode(true)
}
}
I have main package (main.go):
package main
import (
"github.com/gorilla/sessions"
...
)
func main() {
var store = sessions.NewCookieStore([]byte("secret"))
http.HandleFunc("/", routes.Index)
http.ListenAndServe(":8080", nil)
...
And I have another package (index.go):
package routes
import (
"github.com/gorilla/sessions"
..
)
func Index(res http.ResponseWriter, req *http.Request) {
session, _ := store.Get(req, "session-name")
...
How can I get session value from another package? Or should I pass it to my Handler (if yes how to do it?).
I am new in Golang. Please, help.
I generally wrap my dependencies in their own package, which let's me abstract away some of the common things I do. For sessions, I usually use the same session name most of the time, so I would usually have something like this:
package sessions
import (
"os"
gsessions "github.com/gorilla/sessions"
)
var store = gsessions.NewCookieStore([]byte(os.Getenv("SESSION_KEY")))
func Get(req *http.Request) (*gsessions.Session, error) {
return store.Get(req, "default-session-name")
}
func GetNamed(req *http.Request, name string) (*gsessions.Session, error) {
return store.Get(req, name)
}
And then anywhere else you can just import your own sessions package:
import (
"net/http"
"github.com/yourpackage/sessions"
)
func Index(rw http.ResponseWriter, r *http.Request) {
session, err := sessions.Get(r)
if err != nil {
panic(err)
}
session.Values["test"] = "test"
session.Save(r, rw)
}
Even better would be to only return an interface of some sort from sessions, and completely wrap the gorilla/sessions so that aren't dependent on it anywhere except for your own sessions package.
main_test.go
package main_test
import (
"log"
"os"
"testing"
"."
)
func TestMain(m *testing.M) {
a = main.App{}
a.Init(
os.Getenv("TEST_DB_USERNAME"),
os.Getenv("TEST_DB_PASSWORD"),
os.Getenv("TEST_DB_NAME"))
ensureTableExists()
code := m.Run()
clearTable()
os.Exit(code)
}
app.go
package main
import (
"database/sql"
"fmt"
"log"
"github.com/gorilla/mux"
_ "github.com/lib/pq"
)
type App struct {
Router *mux.Router
DB *sql.DB
}
func (a *App) Init(user, password, dbname string) {
connection := fmt.Sprintf("user=%s password=%s dbname=%s", user, password, dbname)
var err error
a.DB, err = sql.Open("postgres", connection)
if err != nil {
log.Fatal(err)
}
a.Router = mux.NewRouter()
}
func (a *App) Run(addr string) { }
main.go
package main
import "os"
func main() {
a := App{}
a.Init(
os.Getenv("APP_DB_USERNAME"),
os.Getenv("APP_DB_PASSWORD"),
os.Getenv("APP_DB_NAME"))
a.Run(":8080")
}
Hey everyone, I am brand new to Golang and working with some tutorials. In the tutorial, they are using the import statement "." which is throwing an error for me. The exact error is "Non-canonical import-path." I tried using a relative path and full path to access the main file in my project but when I use anything other than "." the var a.main.App throws an error saying that main is an unresolved type. My $GOPATH is set to c:/users/me/go/src my project lives in the src folder. I am not entirely sure what is wrong my code at the moment. If it is something glaringly obvious I apologize.
Here is what I am trying to import. This lives in a file called app.go which is called through main.go
type App struct {
Router *mux.Router
DB *sql.DB
}
You don't need to import main for using struct App. You simply change the package of main_test to main then you can able to use that struct, like below i simply passed the main_test file.
package main
import (
"os"
"testing"
)
func TestMain(m *testing.M) {
a := App{}
a.Init(
os.Getenv("TEST_DB_USERNAME"),
os.Getenv("TEST_DB_PASSWORD"),
os.Getenv("TEST_DB_NAME"))
ensureTableExists()
code := m.Run()
clearTable()
os.Exit(code)
}
Here what i get from execute the test:
Success: Tests passed.
I'm trying to write a restful api on golang. For http router I use gin-gonic, to interact with the database I use gorm.
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/postgres"
)
var db *gorm.DB
type Person struct {
ID uint `json:"id"`
FirstName string `json:"firstname"`
LastName string `json:"lastname"`
}
func main() {
// NOTE: See we’re using = to assign the global var
// instead of := which would assign it only in this function
db, err := gorm.Open("postgres", fmt.Sprintf("host=localhost sslmode=disable user=postgres password="))
if err != nil {
fmt.Println(err)
}
defer db.Close()
db.AutoMigrate(&Person{})
r := gin.Default()
r.GET("/people/", GetPeople)
r.GET("/people/:id", GetPerson)
r.POST("/people", CreatePerson)
r.Run(":8080")
}
func CreatePerson(c *gin.Context) {
var person Person
c.BindJSON(&person)
db.Create(&person)
c.JSON(200, person)
}
func GetPerson(c *gin.Context) {
id := c.Params.ByName("id")
var person Person
if err := db.Where("id = ?", id).First(&person).Error; err != nil {
c.AbortWithStatus(404)
fmt.Println(err)
} else {
c.JSON(200, person)
}
}
func GetPeople(c *gin.Context) {
var people []Person
if err := db.Find(&people).Error; err != nil {
c.AbortWithStatus(404)
fmt.Println(err)
} else {
c.JSON(200, people)
}
}
How do I split the code into multiple files so that a separate resource is in a separate file? How to use the router and database in another file?
UPDATE
With structure like this:
.
└── app
├── users.go
├── products.go
└── main.go
I have 2 problems:
db == nil in products.go and users.go
Redeclaration function (get, create ...) in different files, this solve by prefix in function declaration like CreateUser, CreateProduct, etc. But it may be solved by put code into another packages
main.go
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/postgres"
)
var (
db *gorm.DB
r *gin.Engine
)
func init() {
db, err := gorm.Open("postgres", fmt.Sprintf("host=localhost sslmode=disable user=postgres password="))
if err != nil {
fmt.Println(err)
}
defer db.Close()
r = gin.Default()
}
func main() {
r.Run(":8080")
}
products.go
package main
import (
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
)
type Product struct {
gorm.Model
Code string
Price uint
}
func init() {
db.AutoMigrate(&Product{}) // db -> nil
r.GET("/products", get)
}
func get(c *gin.Context) {
var product Product
db.First(&product, 1)
c.JSON(200, gin.H{
"product": product,
})
}
users.go
package main
import (
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
)
type User struct {
gorm.Model
Name string
}
func init() {
db.AutoMigrate(&User{}) // db -> nil
r.GET("/users", get)
}
// ./users.go:19: get redeclared in this block
// previous declaration at ./products.go:20
func get(c *gin.Context) {
var user User
db.First(&user, 1)
c.JSON(200, gin.H{
"user": user,
})
}
Since your db var is defined at the package level it's basically a global for that package an can be referenced in any file that lives in that package.
For example, in a project like this:
.
└── app
├── a.go
├── b.go
├── c.go
└── main.go
If db is defined inside main.go at the package level, as in your example, then code in files a.go, b.go, and c.go can use db.
It works the other way as well, any resource handlers defined in a.go, b.go, and c.go can be reference in main.go. Which means that in each of those files you can define a function that takes a router, the gin router, and sets the corresponding handlers, then inside main.go's main function you call those functions passing in the router r.
Update
First off, you're calling defer db.Close() inside of you init function, which means that right after init returns your db gets closed which is absolutely not what you want. Using defer db.Close() in main is fine because main terminates when your app terminates, closing the db at that point makes sense, but when init terminates your app didn't even start properly, the main is just getting executed and you still need your db.
If you want to use the init functions in each file to do initialization specific to that file, you have to ensure that whatever those init functions depend on, is initialized before they get executed.
In your example all of your init functions depend on db and r so you need to make sure these two are not nil. I'm not exactly sure what, in Go, the order of execution is for multiple init functions in a single package but what I know for sure is that package level variable expressions get initialized before the init functions are executed.
So what you can do is to use a function call to initialize the two package level variables like so:
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/postgres"
)
var (
db = func() *gorm.DB {
db, err := gorm.Open("postgres", fmt.Sprintf("host=localhost sslmode=disable user=postgres password="))
if err != nil {
// if you can't open a db connection you should stop the app,
// no point in continuing if you can't do anything useful.
panic(err)
}
return db
}() // <- call the anon function to get the db.
r = gin.Default()
)
func main() {
// you can call defer db.Close() here but you don't really need to
// because after main exists, that is, your app terminates, db
// will be closed automatically.
r.Run(":8080")
}
As to your second problem, in Go init is a special case and by that I mean that you can have multiple init functions inside a single package, and even inside a single file. This is not true of any other identifiers that you declare.
That means that inside a package, and declared at the package level, you can have only one db identifier, one get identifier, only one User identifier, etc. Whether you use suffiex e.g. getUser or packages user.Get is entirely up to you.
Note that you can redeclare an identifier in another scope, let's say you have type User struct { ... at the package level, and then a function declared in the same pacakge can inside its own scope declare a variable like so var User = "whatever", although it's probably not the best idea it will compile.
For more details see:
Package initialization
Update 2
If you want to split your code into multiple packages you just put your files into separate folders and make sure that the package declaration at the top of your file has the correct package name.
Here's an example:
└── app/
├── main.go
├── product/
│ ├── product.go
│ └── product_test.go
└── user/
├── user.go
└── user_test.go
Now your app/user/user.go code could look something like this.
package user
import (
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
)
var db *gorm.DB
type User struct {
gorm.Model
Name string
}
// custom and exported Init function, this will not be called automatically
// by the go runtime like the special `init` function and therefore must be called
// manually by the package that imports this one.
func Init(gormdb *gorm.DB, r *gin.Engine) {
db = gormdb // set package global
db.AutoMigrate(&User{})
r.GET("/users", get)
}
func get(c *gin.Context) {
var user User
db.First(&user, 1)
c.JSON(200, gin.H{
"user": user,
})
}
your app/product/product.go ...
package product
import (
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
)
var db *gorm.DB
type Product struct {
gorm.Model
Code string
Price uint
}
// custom and exported Init function, this will not be called automatically
// by the go runtime like the special `init` function and therefore must be called
// manually by the package that imports this one.
func Init(gormdb *gorm.DB, r *gin.Engine) {
db = gormdb // set package global
db.AutoMigrate(&Product{})
r.GET("/products", get)
}
func get(c *gin.Context) {
var product Product
db.First(&product, 1)
c.JSON(200, gin.H{
"product": product,
})
}
And your entry point at app/main.go ...
package main
import (
"fmt"
// This assumes that the app/ folder lives directly in $GOPATH if that's
// not the case the import won't work.
"app/product"
"app/user"
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/postgres"
)
func main() {
db, err := gorm.Open("postgres", fmt.Sprintf("host=localhost sslmode=disable user=postgres password="))
if err != nil {
fmt.Println(err)
}
defer db.Close()
r := gin.Default()
// manually initialize imported packages
user.Init(db, r)
product.Init(db, r)
r.Run(":8080")
}
I'm trying to return a instance from the gorm.Open() return it i'm getting following error
controllers/db.go:34: cannot assign *gorm.DB to dc.DB (type gorm.DB) in multiple assignment
This is the db.go controller
package controllers
import (
//"fmt"
_ "github.com/go-sql-driver/mysql"
//v "github.com/spf13/viper"
"github.com/jinzhu/gorm"
)
type DBController struct {
DB gorm.DB
}
func (dc *DBController) InitDB() {
var err error
dc.DB, err = gorm.Open("mysql","root:12345#tcp(localhost:3306)/api")
if err != nil {
log.Fatalf("Error when connect database, the error is '%v'", err)
}
dc.DB.LogMode(true)
}
func (dc *DBController) GetDB() gorm.DB {
return dc.DB
}
What is reason for this error and how can i fix this?
You need and most probably you want to have a pointer in the structure of controller. Passing structure with a pointer to the database object (gorm.DB) will prevent Go from making a copy of this object (gorm.DB).
Do the following:
type DBController struct {
DB *gorm.DB
}
Now it should work fine.