Building a finance tracking REST API using Go with TDD - Part 1
Tags: go, rest, api, tdd
Published: 2018-09-08 11:34:43

---
title: Building a finance tracking REST API using Go with TDD - Part 1
published: true
description: Building a finance tracking REST API using Go from scratch with test-driven development
tags: go, rest, api, tdd
---

Building a simple REST API using Go always could be challenging from designing the architecture to implement it 'cause there's no standard way to do it and developers (software architectures) are free to do whatever they want which comes with a cost. 

After almost 6 years of building services and REST APIs using RubyOnRails, I realized that developers have a passion for abstraction but that's not always a good choice. For example; If you look at almost every RubyOnRails projects there's too much abstraction and implicit things going on which makes the learning curve far more of a curve, starting pretty shallow.

Today I'm going to show you a different perspective of designing REST APIs with Go which like a good parent instead of allowing us to do anything teaches to practice, patience, and perseverance.


Let's get started, at first every Go project must be created in `$GOPATH`. My `$GOPATH` is at, ~~`~/go/src/`~~ `~/go/` . But we're going to create the project under `~/go/src/github.com/budget-api` be able to download the project using `go get`.

So we're going to run these commands, in CLI.

```bash
cd ~/go/src/github.com/azbshiri
mkdir budget-api
touch main.go
```

(If you don't have `github.com/username`, you would create it using `mkdir -p github.com/username` after changing the directory to `~/go/src/`)


Okay, we're going to use PostgreSQL as our database and Gorilla toolkit to help us build a RESTful API. For now, we just need `github.com/go-pg/pg` to be able to connect to PostgreSQL and `github.com/gorilla/mux` for routing.

```bash
go get github.com/go-pg/pg
go get github.com/gorilla/mux
```

Alright now that we have the prerequisites, we should think how we're going to use them and design the architecture.

I'm going to use a `struct` as a container for controller/handler actions called `server` to be able to access database, router and etc. So I create a new file called `server.go` and put the declaration in it.

```go
// server.go
package main

type server struct {
    db *pg.DB
    mux *mux.Router
}
```

And create a factory function to initialize a new server (that would help in testing which I'll show in the future)

```go
// server.go
package main


type server struct {
    db *pg.DB
    mux *mux.Router
}

func newServer(db *pg.DB, mux *mux.Router) *server {
	s := server{db, mux}
	s.routes() // register handlers
	return &s
}
```

We need a `routes.go` to declare paths for APIs

```go
// routes.go
func (s *server) routes() {
    // register handlers here
}
``` 

Now we have a file hierarchy like the below

```
.
├── main.go
├── rotues.go
└── server.go
```

To be able to run the server we should implement `http.Handler` interface so we add a new pointer function to `server` called `ServeHTTP` and serve registered handlers in the router (`mux`)

```go
// server.go
package main

import (
	"net/http"

	"github.com/go-pg/pg"
	"github.com/gorilla/mux"
)

type server struct {
	db     *pg.DB
	mux    *mux.Router
}

func newServer(db *pg.DB, mux *mux.Router) *server {
	server := server{db, mux}
	server.routes() // register handlers
	return &server
}

func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	s.mux.ServeHTTP(w, r)
}
```

Now we can update the `main.go` file to run the server

```go
package main

import (
	"net/http"

	"github.com/go-pg/pg"
	"github.com/gorilla/mux"
)

func main() {
	db := pg.Connect(&pg.Options{
		User:     "alireza",
		Password: "alireza",
	})

	mux := mux.NewRouter()
	server := newServer(db, mux)
	http.ListenAndServe(":8080", server)
}
```

At this point, we don't have any registered handlers which would lead to an API so we have just a useless running server but we have the standard, to begin with which is a great thing to speed up the development process.

Due to I'm a TDD guru, so let's just begin with a test to write our first API. As you know we're going to build a finance tracking REST API so we're going to have `Budget` business model and call the API endpoint `/budgets`.

```go
// budgets_test.go
package main

import (
	"net/http"
	"os"
	"testing"

	"github.com/azbshiri/common/test"
	"github.com/go-pg/pg"
	"github.com/gorilla/mux"
	"github.com/stretchr/testify/assert"
)

var testServer *server

func TestMain(m *testing.M) {
	testServer = newServer(
		pg.Connect(&pg.Options{
			User:     "alireza",
			Password: "alireza",
			Database: "alireza",
		}),
		mux.NewRouter(),
	)
	os.Exit(m.Run())
}

func TestGetBudgets(t *testing.T) {
	res, err := test.DoRequest(testServer, "GET", "/budgets", nil)
	assert.NoError(t, err)
	assert.Equal(t, res.Code, http.StatusOK)
}
```

To write the above test we used two libraries `github.com/stretchr/testify/assert` and `github.com/azbshiri/common/test` the first one is for making assertion easier and the second one is a little bit complicated which I built myself to reduce duplication of initializing of a `http.Request` and a `httptest.ResponseRecorder` and pass them to the existing router to be able to capture the response (That's normal way of writing integration tests in Go for further information you can check out https://golang.org/src/net/http/httptest/example_test.go).

Run the test

```
go test -v .
=== RUN   TestGetBudgets
--- FAIL: TestGetBudgets (0.00s)
        budgets_test.go:42:
                        Error Trace:    budgets_test.go:42
                        Error:          Not equal:
                                        expected: 404
                                        actual  : 200
                        Test:           TestGetBudgets
FAIL
exit status 1
FAIL    github.com/azbshiri/budget-api  0.018s

shell returned 1
```

Phew! The plan was to cover everything in a single post but honestly this is getting longer than anticipated so kindly permit me to break this into a series of posts.


Once again, thanks for reading and don’t forget to leave your questions and/or suggestions as comments below.