go |sql |api

Basic Go HTTP server with Postgres

bmpickford

Benjamin

Posted on August 16, 2019

Basic Go HTTP server with Postgres

I've found myself creating simple Golang microservices using the same basic design to start out. In this post I'll go through the steps from scratch to help create a simple starting point for a Go microservice or API with the ability to create a customer

Throughout this brief tutorial, I have purposely tried to keep it as minimal as possible to get to know the native packages (net/http, encoding/json)


Basic HTTP server

So presuming you have to go environment all setup, let's start by creating a main.go file in our new project with a basic http server

package main

import (
    "log"
    "net/http"
)

const port = "8080"

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

Enter fullscreen mode Exit fullscreen mode

From here, if you run go run . you shouldn't get any errors and should be a good indicator your go setup is correct

So now we have a basic server, it's time to setup a handler to check our configurations are all good. Here we will create a simple response for GET /. To do this, add this handler function to main.go:

func handleHomePage(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, the site is running :)")
}
Enter fullscreen mode Exit fullscreen mode

And then this to your main() function:

http.HandleFunc("/", homePage)
Enter fullscreen mode Exit fullscreen mode

Your main.go file should now look like:

package main

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

func main() {
    http.HandleFunc("/", handleHomePage)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

func handleHomePage(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, the site is running :)")
}

Enter fullscreen mode Exit fullscreen mode

Now to test this works, hit the server with curl using curl -X GET 0.0.0.0:8080/ and your response should be Hello, the site is running :)


Postgres setup

So now we have a basic HTTP server, it's time to add our postgres DB. Assuming you have postgres already installed, lets add a Database and a table. To do this, open postgres in the command line and execute:

CREATE DATABASE go_project;
CREATE TABLE customer (id SERIAL, username VARCHAR NOT NULL);
Enter fullscreen mode Exit fullscreen mode

Go/Postgres connection

To setup our connection, we will be using the pq package for golang (use go get github.com/lib/pq to download the package). We will not create a new file called postgres.go, that will contain a singleton that will be used in our subsequent files to access the database.

package main

import (
    "database/sql"
    "log"

    _ "github.com/lib/pq"
)

var db *sql.DB

func GetDB() *sql.DB {
    var err error

    if db == nil {
        connStr := "user=postgres dbname=go_project"
        db, err = sql.Open("postgres", connStr)
        if err != nil {
            panic(err)
        }
    }

    return db
}

Enter fullscreen mode Exit fullscreen mode

What we are doing here, is creating a connection using the name of our database and a user for our postgres instance. This might change depending on your setup, and may need some authentication


Finishing our handlers

Now it's time to expand our application to include some real logic. To start, we have to create our Customer struct. Firstly though, we have to create a new file for our handlers. I have created one called handler.go but you can name it anything you like. In this file, add the package name and our new struct as follows:

package main

type Customer struct {
    ID       int
    Username string
}
Enter fullscreen mode Exit fullscreen mode

Now it's time to add our handler. For this, we need to create a decoder that will decode the body of our request (which will be JSON format) into our struct. This can be done using the following snippet:

func handleCustomerPost(w http.ResponseWriter, r *http.Request) {
    var customer Customer

    decoder := json.NewDecoder(r.Body)
    err := decoder.Decode(&customer)
    if err != nil {
        panic(err)
    }
    w.WriteHeader(201)
}

Enter fullscreen mode Exit fullscreen mode

Now we have the ability to decode a request body into a Customer model, it's time to add our handler to main.go as we did with our root response. This time though, we have to filter the POST request because the native http package doesn't have the functionality for that. If you add gorilla mux, this can be handled for you, but for this case I want to keep it as minimal as possible. So we need to add another handler in main, as follows:

func handleCustomerRequest(w http.ResponseWriter, r *http.Request) {
    if r.Method == http.MethodPost {
        handleCustomerRequest(w, r)
    }
    w.WriteHeader(201)
}

Enter fullscreen mode Exit fullscreen mode

and a call to it in the main() function as such:

http.HandleFunc("/customer", handleCustomerRequest)
Enter fullscreen mode Exit fullscreen mode

Linking it all together

Now we need to create our query for saving a customer. To do this, we need to create a new file (i called mine database.go), and add the following function that takes in a username

package main

func SaveCustomer(username string) error {
    db := GetDB()
    _, err := db.Exec("INSERT INTO customer (username) VALUES($1)", username)
    return err
}

Enter fullscreen mode Exit fullscreen mode

And now in handler.go, call this function using the customers username as follows:

err = SaveCustomer(customer.Username)
if err != nil {
    panic(err)
}
Enter fullscreen mode Exit fullscreen mode

Now it's time to test our functionality so far. So start up your go service using go run . again. Now if you hit your endpoint, you should get a 201, and a customer with an ID and username in your database

Conclusion

I hope you have learnt something about creating a basic http server in golang. If you want to see the full codebase, see https://github.com/bmpickford/goHttpServer

💖 💪 🙅 🚩
bmpickford
Benjamin

Posted on August 16, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related