1
votes

I am trying to do a REST API in Go. The query works when placed within the main handler (shows in terminal), but gives error in the browser when moved to the desired handler.

localhost:8080/ <------ Browser gives an error (not showing anything) and the terminal shows several error messages - one of them is:

http: panic serving [::1]:51100: runtime error: invalid memory address or nil pointer dereference

localhost:8080/getuser <---gives correct response

Getuser

package main

import (
    "fmt"
    "log"
    "net/http"
    "database/sql"
    _ "github.com/lib/pq"
)

const (
    host     = "127.0.0.1"
    port     = 5432
    user     = "test"
    password = "pw"
    dbname   = "Test"
)

var db *sql.DB

type User struct {
    USER_ID   string
    USER_NAME  string
}

func handleRequests() {
    http.HandleFunc("/", index)
    http.HandleFunc("/getuser", Getuser)
}

func index(w http.ResponseWriter, r *http.Request){
    rows, err := db.Query(`SELECT "USER_ID","USER_NAME" FROM users`)
    if err != nil {
        http.Error(w, http.StatusText(500), 500)
        return
    }
    defer rows.Close()

    for rows.Next() {
        user := User{}
        err := rows.Scan(&user.USER_ID, &user.USER_NAME) 
        if err != nil {
            http.Error(w, http.StatusText(500), 500)
    }
        fmt.Fprintf(w, "%s, %s\n", user.USER_ID, user.USER_NAME)
    }

}

func Getuser(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Getuser")   // <-------------------------this works!
}

func main() {
    handleRequests()

    psqlInfo := fmt.Sprintf("host=%s port=%d user=%s "+
        "password=%s dbname=%s sslmode=require",
        host, port, user, password, dbname)

    db, err := sql.Open("postgres", psqlInfo)
    if err != nil {
        log.Fatalln(err)
    }

    if err != nil {
        panic(err)
    }
    defer db.Close()

    err = db.Ping()
    if err != nil {
        panic(err)
    }

    log.Fatal(http.ListenAndServe(":8080", nil))
  }

EDIT

The complete error list in terminal

created by net/http.(*Server).Serve /usr/local/go/src/net/http/server.go:2795 +0x27b 2018/03/23 08:36:39 http: panic serving [::1]:51390: runtime error: invalid memory address or nil pointer dereference goroutine 36 [running]: net/http.(*conn).serve.func1(0xc4201d80a0) /usr/local/go/src/net/http/server.go:1726 +0xd0 panic(0x1294460, 0x1471840) /usr/local/go/src/runtime/panic.go:505 +0x229 database/sql.(*DB).conn(0x0, 0x13277e0, 0xc42009e020, 0xc420028f01, 0xc4200a0218, 0xc4200a0220, 0xc4201e8b01) /usr/local/go/src/database/sql/sql.go:1015 +0x3a database/sql.(*DB).query(0x0, 0x13277e0, 0xc42009e020, 0x12f7553, 0x26, 0x0, 0x0, 0x0, 0x12ec601, 0x8, ...) /usr/local/go/src/database/sql/sql.go:1437 +0x66

database/sql.(*DB).QueryContext(0x0, 0x13277e0, 0xc42009e020, 0x12f7553, 0x26, 0x0, 0x0, 0x0, 0x10825ed, 0xc4201d0180, ...) /usr/local/go/src/database/sql/sql.go:1419 +0xd2 database/sql.(*DB).Query(0x0, 0x12f7553, 0x26, 0x0, 0x0, 0x0, 0xc4201ec070, 0x8000000000000000, 0x0) /usr/local/go/src/database/sql/sql.go:1433 +0x82 main.Getsign(0x13275a0, 0xc4202020e0, 0xc4201f4100) /Users/sibert/go/src/main/api.go:55 +0x65 net/http.HandlerFunc.ServeHTTP(0x1302020, 0x13275a0, 0xc4202020e0, 0xc4201f4100) /usr/local/go/src/net/http/server.go:1947 +0x44 net/http.(*ServeMux).ServeHTTP(0x147cc00, 0x13275a0, 0xc4202020e0, 0xc4201f4100) /usr/local/go/src/net/http/server.go:2337 +0x130 net/http.serverHandler.ServeHTTP(0xc42009b2b0, 0x13275a0, 0xc4202020e0, 0xc4201f4100) /usr/local/go/src/net/http/server.go:2694 +0xbc net/http.(*conn).serve(0xc4201d80a0, 0x13277a0, 0xc4201b4280) /usr/local/go/src/net/http/server.go:1830 +0x651 created by net/http.(*Server).Serve /usr/local/go/src/net/http/server.go:2795 +0x27b 2018/03/23 08:36:39 http: panic serving [::1]:51391: runtime error: invalid memory address or nil pointer dereference goroutine 24 [running]: net/http.(*conn).serve.func1(0xc4200b4b40) /usr/local/go/src/net/http/server.go:1726 +0xd0 panic(0x1294460, 0x1471840) /usr/local/go/src/runtime/panic.go:505 +0x229 database/sql.(*DB).conn(0x0, 0x13277e0, 0xc42009e020, 0xc420024501, 0xc4200a0318, 0xc4200a0320, 0xc420052b01) /usr/local/go/src/database/sql/sql.go:1015 +0x3a database/sql.(*DB).query(0x0, 0x13277e0, 0xc42009e020, 0x12f7553, 0x26, 0x0, 0x0, 0x0, 0x12ec601, 0x8, ...) /usr/local/go/src/database/sql/sql.go:1437 +0x66 database/sql.(*DB).QueryContext(0x0, 0x13277e0, 0xc42009e020, 0x12f7553, 0x26, 0x0, 0x0, 0x0, 0x10825ed, 0xc420140480, ...) /usr/local/go/src/database/sql/sql.go:1419 +0xd2 database/sql.(*DB).Query(0x0, 0x12f7553, 0x26, 0x0, 0x0, 0x0, 0xc42009eb10, 0x8000000000000000, 0x0) /usr/local/go/src/database/sql/sql.go:1433 +0x82 main.Getsign(0x13275a0, 0xc42019e0e0, 0xc42019a300) /Users/sibert/go/src/main/api.go:55 +0x65 net/http.HandlerFunc.ServeHTTP(0x1302020, 0x13275a0, 0xc42019e0e0, 0xc42019a300) /usr/local/go/src/net/http/server.go:1947 +0x44 net/http.(*ServeMux).ServeHTTP(0x147cc00, 0x13275a0, 0xc42019e0e0, 0xc42019a300) /usr/local/go/src/net/http/server.go:2337 +0x130 net/http.serverHandler.ServeHTTP(0xc42009b2b0, 0x13275a0, 0xc42019e0e0, 0xc42019a300) /usr/local/go/src/net/http/server.go:2694 +0xbc net/http.(*conn).serve(0xc4200b4b40, 0x13277a0, 0xc4200a0780) /usr/local/go/src/net/http/server.go:1830 +0x651 created by net/http.(*Server).Serve /usr/local/go/src/net/http/server.go:2795 +0x27b

1
At which line the error occurs? Plus, the error handling part in the index function for rows.Next() in your code is incomplete, you may check and correct it. - vahdet
Post the full errors log please. This error often occurs when you have a nil pointer. - Pie 'Oh' Pah
@pie-oh-pah Done! - sibert
@vahdet "Plus, the error handling part in the index function for rows.Next() in your code is incomplete, you may check and correct it." Any hint? - sibert
Your global db is nil. In Go := declares a new variable, therefore the db in your main function is a different variable from your global db which stays untouched. Then, your index handler, calling db.Query is calling that method on a nil value of the *DB type. Not what you want. - mkopriva

1 Answers

2
votes

What you are encountering is the classic case of variable shadowing.

The := creates a new db variable that shadows your package level db variable. The set value of db only exists within the scope of your main function while your package level db variable remains as a nil pointer, thus when you attempt to perform a method call on nil, in your index handler, you get a panic.

The reason your Getuser handler works has nothing to do with the DefaultServeMux it's simply because you aren't attempting to access a member function on a nil value.

The way to fix this is within your main function to predeclare the err variable and change the := to simple assignment =

func main() {
// ...
    var err error
    // this will now correctly set your package level variable
    db, err = sql.Open("postgres", psqlInfo)
    if err != nil {
        log.Fatalln(err)
    }
// ...
}