I am trying to create a simple API server using fiber + zap + pgx I am getting an error which I am unable to solve.
PS C:\Users\risharan\Documents\GitHub\freya> go run .\src\freya.go
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x0 addr=0x0 pc=0xcdca67]
goroutine 1 [running]:
go.uber.org/zap.(*Logger).check(0x0, 0xc000060500, 0xff0b96, 0x15, 0x0)
C:/Users/risharan/go/pkg/mod/go.uber.org/zap@v1.18.1/logger.go:268 +0x987
go.uber.org/zap.(*Logger).Info(0x0, 0xff0b96, 0x15, 0x0, 0x0, 0x0)
C:/Users/risharan/go/pkg/mod/go.uber.org/zap@v1.18.1/logger.go:191 +0x4b
freya/src/db.Init()
C:/Users/risharan/Documents/GitHub/freya/src/db/db.go:19 +0x6db
main.init.0()
C:/Users/risharan/Documents/GitHub/freya/src/freya.go:15 +0x31
exit status 2
The code so far is;
// package main
func init() {
logger.Init()
env.Init()
db.Init()
}
func main() {
app := fiber.New()
app.Get("/", func(c *fiber.Ctx) error {
return c.SendString("Hello, World!")
})
log.Fatal(app.Listen(":3000"))
}
// package logger
var Say *zap.Logger
func Init() {
Say, _ := zap.NewProduction()
defer Say.Sync()
}
// package db
var Conn *pgxpool.Pool
func Init() {
c, err := pgxpool.Connect(context.Background(), env.Conf["DATABASE_URL"])
if err != nil {
logger.Say.Fatal("Can't connect to DB", zap.String("Details", err.Error()))
} else {
logger.Say.Info("Connected to Database")
}
Conn = c
defer Conn.Close()
var greeting string
err = Conn.QueryRow(context.Background(), "select 'Hello, this is db!'").Scan(&greeting)
if err != nil {
logger.Say.Error("DB Query failed", zap.String("Details", err.Error()))
}
logger.Say.Info("Connected to DB. DB says; ", zap.String("Details", greeting))
}
The lines indicated by the error message
C:/Users/risharan/Documents/GitHub/freya/src/db/db.go:19 +0x6db
main.init.0()
C:/Users/risharan/Documents/GitHub/freya/src/freya.go:15 +0x31
are:
func init() {
logger.Init()
env.Init()
db.Init() // this is the line 15 as shown in error message in package main
}
and
func Init() {
c, err := pgxpool.Connect(context.Background(), env.Conf["DATABASE_URL"])
if err != nil {
logger.Say.Fatal("Can't connect to DB", zap.String("Details", err.Error()))
} else {
logger.Say.Info("Connected to Database") // this is the line 19 in the error message in the package db
}
So far go's error messages have been hard to decode and I am not sure what am i doing wrong here or what i should do to fix my code.
Note: I have been told to go for a Dependency Injection arch. I personally find that pattern rather complex, and as I am still learning go, i'd prefer to keep it as simple as I can for now. So would prefer answers to not advise me to switch patterns, unless it is not possible to fix the current global variable based pattern.
At least two problems in your various Init()
functions.
You're using :=
which declares a new variable within the scope of the function and does not modify the variable that you declared outside of the scope.
You're using defer
inside Init()
which is where the defered function will be executed upon when Init()
returns. This means, essentially, that your Init
creates and then destroys the resource, making it useless for any subsequent code.
If you want to assign to a variable that's declared in an outer scope do NOT use :=
, instead use =
. And do not do things like defer conn.Close()
inside an initializing function if you intend to use conn
outside of that function.
var Say *zap.Logger
func Init() {
Say, _ = zap.NewProduction()
}
var Conn *pgxpool.Pool
func Init() {
c, err := pgxpool.Connect(context.Background(), env.Conf["DATABASE_URL"])
if err != nil {
logger.Say.Fatal("Can't connect to DB", zap.String("Details", err.Error()))
} else {
logger.Say.Info("Connected to Database")
}
Conn = c
var greeting string
err = Conn.QueryRow(context.Background(), "select 'Hello, this is db!'").Scan(&greeting)
if err != nil {
logger.Say.Error("DB Query failed", zap.String("Details", err.Error()))
}
logger.Say.Info("Connected to DB. DB says; ", zap.String("Details", greeting))
}
func init() {
logger.Init()
env.Init()
db.Init()
}
func main() {
defer func() {
db.Conn.Close()
logger.Say.Sync()
}()
app := fiber.New()
app.Get("/", func(c *fiber.Ctx) error {
return c.SendString("Hello, World!")
})
log.Fatal(app.Listen(":3000"))
}