First of all, welcome to my first blog post. Today I want to help you build your first RESTful API in Golang! The first two sections will help you getting started with Go, you may be able to skip these steps.

Installing the go command line tool

Before you can start writing Go code, you need the go command line tool that bundles the compiler and a bunch of other development tools for you. Just visit golang.org/dl/ to fetch the latest version and install it on your workstation. Be sure to yet your $GOPATH correctly!

Printing out the holy mantra

Let start with something simple - why not hello world? Create a new file called hello.go or whatever you want to name it and type the following code in your editor of choice.

package main

import "fmt"

func main() {
	fmt.Println("hello, world")
}

If you want to run your program, just enter go run hello.go to execute it. Pretty straightforward, huh? Let us dive a bit deeper into the structure of your first Go program. Before you continue with this post, I recommend to get a bit more accustomed to the language by doing the Go tour.

Dive into the magic

In the very first line, we declare a package. Packages let us bundle batches of files into one component that lie under a common namespace. Since we want to run our piece of work, it must be in the package main to be executable.

In the second line, we import a package called fmt (spoken like format or fomt, you decide). It is a collection of methods and structures to help us print stuff to stdout and other data streams. In our program we will only use fmt.Println which name is very descriptive: it just prints its arguments separated by spaces and adds a new line to the end.

In the third line, we declare a function. I assumed at the top that you are not completely new to the concepts of programming, so it is pretty much the same thing as in every other language you have used so far. We call the function main, so it will be called right when we run our code. Since we finished the holy mantra, we are blessed to go on with our journey. Now we will start building real server shit!

Designing a simple API

As I mentioned above, the power of the language and its standard library enables us to build server systems insanely fast. We will abuse some of that power and write a simple REST API for a community-run weather service. A client will be able to report and fetch current temperature values for a set of cities.

Example: Report measured temperature in Munich

$ curl -X POST -d @- localhost:8080/reports
{
	"city": "munich",
	"temperature": -12.0,  
	"unit": "celsius"
}
OK

Example: List locations with available reports

$ curl -X GET localhost:8080/reports
["munich"]

Example: Get the latest report from Munich

$ curl -X GET localhost:8080/reports/munich
{"city":"munich","temperature":-12,"unit":"celsius","timestamp":1519520293}

Organizing packages

To make things a bit cleaner, we want to organize our API using the $GOPATH. If you have a GitHub account, you can easily publish packages by setting up a repository in the $GOPATH directory. Let’s assume you have this awesome library called awesome-lib located in $GOPATH/src/github.com/yourgithubaccount/awesome-lib and you have published it under the same repository name on GitHub, everyone can import your library using the statement import "github.com/yourgithubaccount/awesome-lib". There is no need for a bloaty package manager!

We also want to do it this way and create a new folder called $GOPATH/src/github.com/yourgithubaccount/weather-api. In this folder, we once again create a new file called main.go with the following content. You can look up the current snapshot of the package on my GitHub account.

A first blueprint

package main

import (
	"fmt"
	"net/http"

    "github.com/gorilla/mux"
	"github.com/lnsp/go-demos/weather"
)

type weatherAPI struct {
    backend *weather.Service
    mux *mux.Router
}

func (api *weatherAPI) ListCities(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, `["munich"]`)
}

func (api *weatherAPI) CreateReport(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "OK")
}

func (api *weatherAPI) ShowTemperature(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, `{"city":"munich","temperature":-12,"unit":"celsius","timestamp":1519520293}`)
}

func (api *weatherAPI) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    api.mux.ServeHTTP(w, r)
}

func newWeatherAPI(backend weather.Service) *weatherAPI {
    api := &weatherAPI{
        backend: backend,
        mux: mux.NewRouter(),
    }
    api.mux.HandleFunc("/reports", api.ListCities).Methods("GET")
    api.mux.HandleFunc("/reports", api.CreateReport).Methods("POST")
    api.mux.HandleFunc("/reports/{city}", api.ShowTemperature).Methods("GET")
    return api
}

func main() {
    backend := weather.NewInMemoryService()
    handler := newWeatherAPI(backend)
	if err := http.ListenAndServe(":8080", handler); err != nil {
		panic(err)
	}
}

Installing dependencies

Since we have external dependencies this time, you have to run go get -v to install these. If you want to test the system, just run go run main.go in your terminal, open a second tab and run curl localhost:8080/reports to send a request to your server. curl is an awesome tool to easily test simple APIs and we will continue to use it in this blog post.

Inspecting the blueprint

We see once again the package main statement and once again it means that we want to this piece of code to be executable. But this time, the import statement is different. Instead of a single package we give it a list of packages. These combined statements help us to visually group imports and keep our code clean and simple.

In the second import line, there is a package we don’t know about yet: net/http. It provides a complete HTTP stack including an web server, HTTPS / HTTP2 support and a client implementation for sending out requests.

The third and fourth import lines define a third-party package, a package that is not included in the standard library. In our case, we import one of my published packages called weather (the last part of the fully-qualified import path) and the wonderful mux package by the Gorilla project. The weather package was written for this tutorial and exposes a collections of structures that will provide the “business logic” for this project (mostly a simple in-memory datastore).

Spawning an API instance

To easily be able to plug different implementations of the weather service backend into our RESTful API frontend, we create a structure that contains our systems dependencies called weatherAPI. This structure also implements the http.Handler interface, so we can just put it into the handler field of http.ListenAndServe. On instance creation, we have to make sure that our routes are connected in the right way – using the declarative syntax of mux.Router to only allow specific kinds of requests.

The first two http.HandleFunc calls are completely standard, nothing off the ordinary. In the third route we use something special called a route variable. These are provided by the mux.Router and allow us to easily extract parameters from a requests URI path.

About request handlers

The methods thrown into our router are pretty similar, all require a http.ResponseWriter and a *http.Request as parameters. This function signature is defined in the package http as the type http.HandlerFunc. Since Go supports first-class functions, these functions can be thrown around just like another variable or constant. We can use their special signature to directly wire them to our web server.

All of the three handlers have a special role assigned.

  • ListCities, sends back a list of all available measurement locations
  • ShowTemperature, sends back the last measured temperature at the specified place
  • CreateReport, accepts new temperature measurement reports from client

In each handler we use the legless sibling of the fmt.Println method we used earlier called fmt.Fprintln. The only difference between these two is that the fmt.Fprintln version takes an implementation of the io.Writer interface as the first parameter. Luckily, the http.ResponseWriter instance we received from our server implements this exact interface.

In the main method we first initialize our package variable service using a method from the weather package called NewInMemoryService which provides a simple in-memory implementation of the weather.Service interface. Then we call newWeatherAPI, it instantiates a new API handler, sets up the needed routes and returns it.

In case you don’t have it discovered yet: every reference in this post mentioning a library package or an item in one of theses packages, is a link to the matching documentation. Go provides excellent tooling including a documentation tool called godoc. godoc.org, a hosted version of the godoc tool, provides documentation for each publicly available package.

Moving on

I recommend to hack a bit around using the code above. If you don’t have any ideas, here are some of mine.

  • temper with the output, let it print other messages the writer
  • add an additional handler

In the second part of this article series, we will learn about error handling, more standard library packages and JSON encoding and decoding.