DEV Community

Kyle Trahan
Kyle Trahan

Posted on • Updated on

My first Go API

I recently branched out of our current program and built a simple API using Go. Going down this route taught me that I didn't know nearly as much about what was happening in my backend as I thought. I started to discover just how much Ruby/Rails does for you compared to building the API from scratch. I'm going to share my experience and one of the issues I ran into while creating this API.

The first thing I needed to do in order to create my simple API in Go was to create my entry point. In order to do this I declared a package name of main and a func main in order to execute my Go program.

Declaring at the top

package main
Enter fullscreen mode Exit fullscreen mode
func main() {
    connectToDatabase()
    handleRequest()
    defer db.Close()
}
Enter fullscreen mode Exit fullscreen mode

Next up I wanted to connect my API to a PSQL database in order to POST and GET entries from my React-Redux frontend. You'll notice I called this function inside of my entry point in order to make the connection to the database whenever my package is initialized.

func connectToDatabase() {

    var err error
    host := os.Getenv("HOST")
    databaseUsername := os.Getenv("USERNAME")
    password := os.Getenv("PASSWORD")
    database := os.Getenv("DATABASE")
    port := os.Getenv("PORT")

    psqlInfo := fmt.Sprintf("host=%s port=%s user=%s "+
        "password=%s dbname=%s sslmode=disable",
        host, port, databaseUsername, password, database)
    db, err = sql.Open("postgres", psqlInfo)
    if err != nil {
        panic(err)
    }

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

    fmt.Println("Successfully connected!")
}
Enter fullscreen mode Exit fullscreen mode

After I stop running the program I used this method in my func main in order to close any open cursors, free allocated resources, and close underlying files.

defer db.Close()
Enter fullscreen mode Exit fullscreen mode

Next up was creating my routes. I am going to highlight one of them in order to showcase the issue I ran into while creating this API.

func addScores(w http.ResponseWriter, r *http.Request) {

    setupResponse(&w, r)

    switch r.Method {
    case "POST":
        reqBody, _ := ioutil.ReadAll(r.Body)
        fmt.Fprintf(w, "%v", string(reqBody))
        var highscore HighScore
        json.Unmarshal(reqBody, &highscore)

        username := highscore.Username
        score := highscore.Score

        var err error
        sqlStatement := `
            INSERT INTO highscores (username, score)
            VALUES ($1, $2)`
        _, err = db.Exec(sqlStatement, username, score)
        if err != nil {
            panic(err)
        }
    default:
        //method not available error.
        http.Error(w, http.StatusText(405), 405)
    }

}
Enter fullscreen mode Exit fullscreen mode

The main issue I found while creating this API was not setting what specifically should happen whenever different types of Methods are received in the http request. I initially forgot to handle my preflight request, which led to my database receiving double entries each time I sent a POST request. A simple switch statement fixed my issue so whenever my route receives the OPTIONS method it won't create a new row in my table. It will only create the row when receiving a POST method.

One of the final steps for creating this API was to allow access from the frontend to the backend. I set up my response using the following.

func setupResponse(w *http.ResponseWriter, req *http.Request) {
    (*w).Header().Set("Access-Control-Allow-Origin", "*")
    (*w).Header().Set("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, OPTIONS")
    (*w).Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
}
Enter fullscreen mode Exit fullscreen mode

Overall this was a very fun experience trying to learn a new tech on my own for the first time since joining Flatiron School. I learned quite a lot about the request and response cycle. Along with learning that its very important to tell your routes how to deal with the preflight requests, otherwise you may end up with a bunch of a blank rows in your database!

Thanks for taking the time to read this and hope its helpful for someone out there!

Discussion (0)