How I create small Go apps quickly

Topics: Engineering
I often want to create a small web application to test out an idea or demonstrate some thing, and I’ve developed a small toolbox that helps me do so in Go.

I recently created the COVID Report prototype, a small application that connects to the Centers for Medicare & Medicaid Services’ Blue Button API or the Department of Veterans Affairs Lighthouse APIs to look for vaccination records and display them to a user. The COVID Report prototype is open source and available on GitHub. With only about two weeks to build it from start to finish, it had to be built simply and efficiently, and Go was perfect for the task.

Since I’ve done this process a few times, I’ve decided to publish a starter kit that wraps up some of my small tools, and this blog post will show how I created it step by step.

Serving

When I want to build a small go application, I start with something like this:

package main

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

type App struct {
	Port string
}

func (a *App) Start() {
	addr := fmt.Sprintf(":%s", a.Port)
	log.Printf("Starting app on %s", addr)
	log.Fatal(http.ListenAndServe(addr, nil))
}

func env(key, defaultValue string) string {
	val, ok := os.LookupEnv(key)
	if !ok {
		return defaultValue
	}
	return val
}

func main() {
	server := App{
		Port: env("PORT", "8080"),
	}
	server.Start()
}

This is just a skeleton app that doesn’t serve anything. It sets up an application struct with only one member, a variable for the port to serve on. You can give it a port to serve on with the PORT environment variable.

I generally set up my applications to be configured by environment variables instead of command line flags because I find it to be easier to deploy. As soon as I add my first environment variable, I create an .envrc file and a value for it there. I highly recommend using direnv, which will load the variables you have saved into a .envrc file for you every time you enter the directory.

Handlers

As it stands, the tiny application doesn’t have any handlers; it will return a 404 response code for any request made to it.

Go’s http server will not log requests made to it by default, so before I add any handlers I add this little middleware function that logs requests, which I will wrap around all my handlers:

func logreq(f func(w http.ResponseWriter, r *http.Request)) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		log.Printf("path: %s", r.URL.Path)

		f(w, r)
	})
}

This uses Go’s standard log module to print out the path of the request and forward it on to the handler it’s wrapping.

There are fancier logging modules that do all kinds of useful stuff; I’ve used zerolog to great effect, for example. When I’m developing something quickly, though, I try to avoid dependencies and reach for the very simplest thing that could possibly work.

Now that I can log requests, which is vital for debugging, I can create my first handler:

func index(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "hello world\n")
}

// and add this to App.Start to wire it up
func (a *App) Start() {
	http.Handle("/", logreq(index))
	// ... rest as before
}

Another thing to keep in mind when building small apps quickly is that you want to see results to keep yourself from getting stuck. In this application, I knew that I would need to render templates, but I want to see “hello world” in a running app before I get to building anything more complicated.

At this point, you can fire up the app with go run main.go, then test it out:

$ curl http://localhost:8080
hello world

I still get excited to see my programs print “hello world”, almost 25 years after the first one.

Templates

The first “hello world” I ever saw was rendered from a ColdFusion template, and the app I’m building is not going to be fundamentally different from that one in style. Let’s wire up Go’s html/template module to render templates for us.

Create a templates directory, and add a file called index.html that looks like this:


<!DOCTYPE html>
<html>
  <head>
    <title>Home Page</title>
  </head>
  <body>
    Hello <strong>{{.Name}}</strong>
  </body>
</html>

To render a template, I use this function:

func renderTemplate(w http.ResponseWriter, name string, data interface{}) {
	// This is inefficient - it reads the templates from the
	// filesystem every time. This makes it much easier to
	// develop though, so I can edit my templates and the
	// changes will be reflected without having to restart
	// the app.
	t, err := template.ParseGlob("templates/*.html")
	if err != nil {
		http.Error(w, fmt.Sprintf("Error %s", err.Error()), 500)
		return
	}

	err = t.ExecuteTemplate(w, name, data)
	if err != nil {
		http.Error(w, fmt.Sprintf("Error %s", err.Error()), 500)
		return
	}
}

On every call, this function will scan the templates directory and grab every *.html file to parse as a template. This is inefficient, but it trades runtime efficiency for development productivity, a trade I’m happy to make while I’m in the prototyping stage.

Finally, let’s render the template from the index handler:

func index(w http.ResponseWriter, r *http.Request) {
	renderTemplate(w, "index.html", struct {
		Name string
	}{
		Name: "Sonic the Hedgehog",
	})
}

Here’s the full app I’ve built so far, which can now serve a web page! Run go run main.go, and visit http://localhost:8080 in your browser, and you should see “hello Sonic The Hedgehog”.

As I build an app like this, I always want to see progress from each stage, so I make sure to run the app and verify that what I expect to be happening is happening.

Partial templates

As soon as I have two pages, I’m going to want to have templates that include other templates, so it’s worth taking a minute to get that set up. Go’s usually excellent documentation is not quite up to par on the templates module, so it always takes me a little bit to figure out how to do so.

First, create a templates/header.html file:

<!DOCTYPE html>
<html>
  <head>
    <title>Home Page</title>
  </head>
  <body></body>
</html>

Then modify templates/index.html to include it:


{{template "header.html" .}}

Hello <strong>{{.Name}}</strong>
</body>
</html>

Easy peasy.

Reloading

As I write code, I don’t want to bother running the server every time I make a change. My favorite tool for rebuilding the application as I develop it is cortesi/modd. It unfortunately hasn’t been actively developed for some time, but it gets the job done and is simple.

After I install it, I create a modd.conf file:

**/*.go {
    prep: go build -o aprogram
    daemon: ./aprogram
}

This file tells modd that whenever any *.go file changes, it should rebuild aprogram and then run it. Now just run modd in the directory containing main.go and modd.conf, and your app will try to rebuild itself every time you save the file.

Go’s fast compilation is a huge help here, allowing us to rely on a very simple strategy of rebuilding the entire thing on every change. As your application grows larger, you might have to do something smarter and more complicated, but I like to push stupid simple as far as it will go.

Static files

Now that I have an application that can serve HTTP pages built from templates, I’ll want to add some static file handling so I can include CSS or Javascript pages from my HTML. In production, I’ll want to pull them from S3 or some other static file source, but in development it’s nicer to get them from a directory so I can test changes rapidly.

To do so, I make a /static/ directory, add some files in there, and add a handler to my program:

func staticHandler(dir string) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		http.StripPrefix("/static/", http.FileServer(http.Dir(dir))).ServeHTTP(w, r)
	}
}

This function accepts a directory to serve as a static directory and returns an HTTP Handler that will strip /static/ from the start of the URL then serve a file from the file system.

Since I only want to serve static resources from my app in development, I usually will add a STATIC_BASE environment variable. In development, it will just be /static/, but in production it will be something like https://my-bucket.s3.us-west-2.amazonaws.com/, the base URL that I am serving assets from.

To wire that up requires a few steps. First I add a StaticBase member to my App struct:

type App struct {
	Port       string
	StaticBase string
}
//...
    // where I create the App, load the STATIC_BASE env var
	server := App{
		Port:       env("PORT", "8080"),
		StaticBase: env("STATIC_BASE", "/static"),
	}

Then I hang the index function off the App struct, so that it can send the StaticBase to the template:

func (a App) index(w http.ResponseWriter, r *http.Request) {
	renderTemplate(w, "index.html", struct {
		Name       string
		StaticBase string
	}{
		Name:       "Sonic The Hedgehog",
		StaticBase: a.StaticBase,
	})
}

I modify App.Start to conditionally serve static files, and call the index handler from its new location:

func (a App) Start() {
	if a.StaticBase == "/static" {
		log.Printf("serving static assets")
		http.Handle("/static/", logreq(staticHandler("static")))
	}
	http.Handle("/", logreq(a.index))

Create a /static/ directory, then add a style.css file to it:

body {
  width: 760px;
  margin: auto;
  margin-top: 50px;
  font-size: 30px;
}

And, finally, load the stylesheet from the header template:


<!DOCTYPE html>
<html>
  <head>
    <title>Home Page</title>
    <link rel="stylesheet" href="{{.App.StaticBase}}/style.css" />
  </head>
  <body>
    
  </body>
</html>

Conclusion

At this point, I have the bones of a pretty good application development environment. I have an application that I can work on and rapidly see the results, templates that I can pass variables to, and a way to show static assets that allows rapid development locally, but will also scale to production.

I’ve published this skeleton as open source on GitHub, where you can grab it and use it as a starter if it appeals to you, or take away any ideas from it that you like.

More on These Topics