2
votes

I am trying to create an API with Go, gorm and gin. However, using a direct SQL query did work, but it became too cumbersome and I decided to leverage gorm which is fantastic. However after my implementation, I am unable to get the project up again and keep getting error

panic: runtime error: invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation code=0x1 addr=0xb0 pc=0x137b606]

I have pasted my code with packages here. I would be glad if someone could point me to the issue.

PostgresDoa.go

package postgres

import (
    "fmt"
    "log"
    "net/http"

    "github.com/AdieOlami/bookstore_users-api/domain/users"
    "github.com/gin-gonic/gin"
    "github.com/jinzhu/gorm"
)

type Sever struct {
    DB     *gorm.DB
    Router *gin.Engine
}

func (server *Sever) Initialize(Dbdriver, DbUser, DbPassword, DbPort, DbHost, DbName string) {
    var err error

    if Dbdriver == "mysql" {
        DBURL := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8&parseTime=True&loc=Local", DbUser, DbPassword, DbHost, DbPort, DbName)
        server.DB, err = gorm.Open(Dbdriver, DBURL)
        if err != nil {
            fmt.Printf("Cannot connect to %s database", Dbdriver)
            log.Fatal("This is the error:", err)
        } else {
            fmt.Printf("We are connected to the %s database", Dbdriver)
        }
    }

    if Dbdriver == "postgres" {
        DBURL := fmt.Sprintf("host=%s port=%s user=%s dbname=%s sslmode=disable password=%s", DbHost, DbPort, DbUser, DbName, DbPassword)
        server.DB, err = gorm.Open(Dbdriver, DBURL)
        if err != nil {
            fmt.Printf("Cannot connect to %s database", Dbdriver)
            log.Fatal("This is the error:", err)
        } else {
            fmt.Printf("We are connected to the %s database", Dbdriver)
        }
    }

    server.DB.Debug().AutoMigrate(&users.User{}) //database migration
    server.Router = gin.Default()
}

func (server *Sever) Run(addr string) {
    fmt.Println("Listening to port 8080")
    log.Fatal(http.ListenAndServe(addr, server.Router))
}

UserDto.go

package users

import (
    "strings"

    "github.com/AdieOlami/bookstore_users-api/domain/base"
    "github.com/AdieOlami/bookstore_users-api/utils/errors"
    "github.com/jinzhu/gorm"
    uuid "github.com/satori/go.uuid"
)

type User struct {
    base.Base
    UserID    uuid.UUID `gorm:"type:uuid;column:userId;not null;" json:"userId"`
    FirstName string    `gorm:"size:255;not null;unique;column:firstName" json:"firstName"`
    LastName  string    `gorm:"size:255;not null;unique;column:lastName" json:"lastName"`
    Email     string    `gorm:"size:100;not null;unique;column:email" json:"email"`
}

func (user *User) Validate() *errors.Error {
    user.Email = strings.TrimSpace(strings.ToLower(user.Email))
    if user.Email == "" {
        return errors.NewBadRequestError("invalid email address")
    }
    return nil
}

func (user *User) SaveUser(db *gorm.DB) *errors.Error {

    var err error
    err = db.Debug().Create(&user).Error
    if err != nil {
        return errors.NewInteralServerError(err.Error())
    }
    return nil
}

UserService.go

package services

import (

    "github.com/AdieOlami/bookstore_users-api/domain/users"
    "github.com/AdieOlami/bookstore_users-api/utils/errors"
)

func (server *Server) CreateUser(user users.User) (*users.User, *errors.Error) {

    if err := user.Validate(); err != nil {
        return nil, err
    }

    if err := user.SaveUser(server.DB); err != nil {
        return nil, err
    }
    return &user, nil
}

UserController.go

package users

import (
    "net/http"
    "strconv"

    "github.com/AdieOlami/bookstore_users-api/domain/users"
    "github.com/AdieOlami/bookstore_users-api/services"
    "github.com/AdieOlami/bookstore_users-api/utils/errors"
    "github.com/gin-gonic/gin"
)

var (
    server = services.Server{}
)

// CREATE USER
func CreateUser(c *gin.Context) {
    var user users.User

    if err := c.ShouldBindJSON(&user); err != nil {
        // TODO: hnadle json error return bad request
        err := errors.NewBadRequestError("invalid json body")
        c.JSON(err.Status, err)
        // fmt.Println(err.Error())
        return
    }
    result, saveErr := server.CreateUser(user)
    if saveErr != nil {
        // TODO: handle user createing error
        c.JSON(saveErr.Status, saveErr)
        return
    }
    c.JSON(http.StatusCreated, result)
}

Routes.go

package app

import (
    "github.com/AdieOlami/bookstore_users-api/controllers/users"
    "github.com/AdieOlami/bookstore_users-api/database/postgres"
)

var (
    server = postgres.Sever{}
)

func initializeRoutes() {

    server.Router.POST("/users", users.CreateUser)
}

Application.go

package app

import (
    "os"

    "github.com/AdieOlami/bookstore_users-api/seed"

    _ "github.com/jinzhu/gorm/dialects/mysql"    //mysql database driver
    _ "github.com/jinzhu/gorm/dialects/postgres" //postgres database driver
)

func StartApplication() {

    server.Initialize(os.Getenv("DB_DRIVER"), os.Getenv("DB_USER"), os.Getenv("DB_PASSWORD"), os.Getenv("DB_PORT"), os.Getenv("DB_HOST"), os.Getenv("DB_NAME"))

    seed.Load(server.DB)
    server.Run(":8088")
    initializeRoutes()
}

in my Main.go

func main() {
    app.StartApplication()
}
1
Go normally also writes a stacktrace with filenames and linenumbers to the output. Using those, you should be able to see exactly where things go wrong. If you can add the stacktrace, somebody might be able to see where the issue is.Jory Geerts

1 Answers

1
votes

As far as I can understand from your code. You declared below function in Services package whereas Server object is declared in the Postgres package. This causes dereferencing of pointer server *Server to an invalid address. you have to declare this function in posgres package.

func (server *Server) CreateUser(user users.User) (*users.User, *errors.Error) {

    if err := user.Validate(); err != nil {
        return nil, err
    }

    if err := user.SaveUser(server.DB); err != nil {
        return nil, err
    }
    return &user, nil
}