My Go Toolset
Tags: go, development
Published: 2020-04-17 12:42:49

When developing some software, you very often tend to use some 3rd party tools or libraries to enable yourself to focus more on the task at hand. And this makes perfect sense, imagine if you had to write your own HTTP server everytime you wanted to develop a web application. Or a router, or, or, or... This would be insane and nothing would get done. Ever.

We as developers also grow used to certain tools, and don't switch much between them, unless we really have to due to many reasons. And here is the list of my tools and libraries that I tend to use, with short description of each of thembellow:

* [go-delve/delve](https://github.com/go-delve/delve)
* [spf13/cobra](https://github.com/spf13/cobra)
* [spf13/viper](https://github.com/spf13/viper)
* [sirupsen/logrus](https://github.com/sirupsen/logrus)
* [gorilla/mux](https://github.com/gorilla/mux)
* [adams-sarah/test2doc](https://github.com/adams-sarah/test2doc)

Lets dig in!

## go-delve/delve - Debugging

When it comes to debugging Go applications, [go-delve/delve](https://github.com/go-delve/delve) is my go-to library. To start debugging your source code, simply execute:

```shell
dlv debug gitlab.com/youruser/project/
```

Or to debug a built binary:
```shell
dlv exec $GOPATH/bin/built_bin
```

Even debug your pesky tests:
```shell
dlv test gitlab.com/youruser/project
```

Now you can start setting breakpoints using `break pkg_name.FuncName` or `break filename.go:<linenum>` and then starting the execution by calling `continue`.

You can also start the debugging session with the `--headless` flag to start the debugger in headless mode and connect to it with your editor or IDE.

## spf13/cobra - CLI commands

Creating a CLI interface to my applications is really simple when using the [spf13/cobra](https://github.com/spf13/cobra) library, all you need to do is execute:

```shell
cobra init nameofexec --pkg-name gitlab.com/yourname/yourproject
```

And *cobra* will create everything, the directory structure inside a new directory named **nameofexec**, the root command inside **nameofexec/cmd/root.go**, all you need to do is open the file, edit the descriptions and tell the command what you want it to actually do.

Need a new command to go with your executable? No problem:

```shell
cobra add mycmd
```

This will create the **nameofexec/cmd/mycmd.go** file and register the command. Again, all you need to do is set descriptions and tell it what to do.

Of course you can also add flags and options to your executable and/or commands, but this is beyond the scope of this article.

## spf13/viper - Configuration

Need to parse configuration files? Need to parse configuration from a distributed key-value store like *etcd*? Look no further, [spf13/viper](https://github.com/spf13/viper) got you covered! If you're using *spf13/cobra* from above, it already imports and configures *spf13/viper* for you, if not, configuration is really simple:

```go
// initConfig reads in config file and ENV variables if set.
func initConfig() {
	if cfgFile != "" {
		// Use config file from the flag.
		viper.SetConfigFile(cfgFile)
	} else {
		// Find home directory.
		home, err := homedir.Dir()
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

		// Search config in home directory with name ".yourproject" (without extension).
		viper.AddConfigPath(home)
		viper.SetConfigName(".yourproject")
	}

	viper.AutomaticEnv() // read in environment variables that match

	// If a config file is found, read it in.
	if err := viper.ReadInConfig(); err == nil {
		fmt.Println("Using config file:", viper.ConfigFileUsed())
	}
}
```

Accessing the configuration is even simpler:

```go
import (
    "fmt"

    "github.com/spf13/viper"
)

func main() {
    fmt.Println(viper.GetString("my.key"))
}
```

It supports multiple file types, *JSON*, *YAML*, *TOML*,... Whatever you need!

## sirupsen/logrus - Logging

To log my messages nicely under different levels or even to different destinations I let [sirupsen/logrus](https://github.com/sirupsen/logrus) handle it. It doesn't even need any configuration, you can just use it from the get go:

```go
package main

import "github.com/sirupsen/logrus"

func main() {
    logrus.Info("Hello!")

    details := "important stuff"
    logrus.WithField("details", details).Info("I've got something important to tell you")

    err := anything.Do()
    if err != nil {
        logrus.WithError(err).Fatal("I couldn't do anything!")
    }
}
```

Of course this is just basic usage, you can set the desired log level to ignore messages that are in lower levels and log just the errors, you have countless hooks readily available to help you send your log messages to any service you want, and if the hook isn't available, it's really easy to write one yourself.

## gorilla/mux - HTTP Router

When writing a web application or a RESTful API, you need a HTTP router and dispatcher. I tend to always use [gorilla/mux](https://github.com/gorilla/mux) in my projects. It satisfies all my needs, and is very simple to use:

```go
package main

import (
    "fmt"
    "net/http"

    "github.com/gorilla/mux"
)

func main() {
    rtr := mux.NewRouter()

    rtr.HandleFunc("/hello", func(w http.ResponseWriter, req *http.Request) {
        fmt.Fprint(w, "Hello, World!")
    })

    http.ListenAndServe(":8080", rtr)
}
```

Compile, run and visit `http://localhost:8080/hello` and there you go! Want to make it more personal? No problem let's just change the above `HandleFunc` to include a parameter or leave the above one in place and create a new route:

```go
// ...
rtr.HandleFunc("/hello/{name}", func(w http.ResponseWriter, req *http.Request) {
    fmt.Fprintf(w, "Hello, %s!", mux.Vars(req)["name"])
})
// ...
```

Compile, run and visit `http://localhost:8080/hello/foo`.

## adams-sarah/test2doc - API documentation

I must say I simply love [adams-sarah/test2doc](https://github.com/adams-sarah/test2doc)! Writing tests can be a daunting task, and then you still need to write documentation. Well here comes **test2doc** to the rescue! Simply write up a test for a handler using the **test2doc** server:

```go
package main

import (
	"testing"

	"github.com/adams-sarah/test2doc/test"
	"github.com/adams-sarah/test2doc/vars"
)

func TestMyHandler(t *testing.T) {
	// init the router and register a handler to a route
	rtr := mux.NewRouter()
	rtr.HandleFunc("/hello", helloHandler)

	// register the mux extractor
	test.RegisterURLVarExtractor(vars.MakeGorillaMuxExtractor(rtr))

	// start the server and defer the Finish call
	var err error
	server, err = test.NewServer(rtr)
	if err != nil {
		t.Errorf("unable to create test server, %s", err)
	}
	defer server.Finish()

	// get the named route from mux and get its URL
	urlPath, err := rtr.Get("hello").URL()
	if err != nil {
		t.Errorf("unable to get route URL, %s", err)
	}

	resp, err := http.Get(server.URL + urlPath.String())
	if err != nil {
		t.Errorf("unable to process the request, %s", err)
	}
}
```

Now simply execute the test with `go test` and you should get a new file in your directory ending with an `.apib` extension.

## Web application directory layout

I have also prepared a small bare-bones project that I tend to use often when starting a new project, since I already have a lot of the above tools and libraries already incorporated, as well as some deployment options. It is personalized to my own use, but can probably be useful for someone else too, since it does not enforce anything. Please check it out at my [GitLab repository](https://gitlab.com/slax0rr/go-web-scaffold) and feel free to use it if you find it useful!