Basic Go HTTP server with Postgres
Tags: go, sql, api
Published: 2019-08-16 11:44:23

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

```go
package main

import (
	"log"
	"net/http"
)

const port = "8080"

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

```
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`:
```go
func handleHomePage(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello, the site is running :)")
}
```

And then this to your `main()` function:
```go
http.HandleFunc("/", homePage)
```


Your `main.go` file should now look like:
```go
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 :)")
}

```

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);
```

-----

### 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.

```go
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
}

```
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:

```go
package main

type Customer struct {
	ID       int
	Username string
}
```

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:
```go
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)
}

```

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:
```go
func handleCustomerRequest(w http.ResponseWriter, r *http.Request) {
	if r.Method == http.MethodPost {
		handleCustomerRequest(w, r)
	}
    w.WriteHeader(201)
}

```
and a call to it in the `main()` function as such:
```go
http.HandleFunc("/customer", handleCustomerRequest)
```

----

### 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
```go
package main

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

```

And now in `handler.go`, call this function using the customers username as follows:
```go
err = SaveCustomer(customer.Username)
if err != nil {
	panic(err)
}
```

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