April 9, 2015

serving static, embedded assets with go

Today I’d like to share a tiny example of why I’ve come to love golang as a programming language during the last two years: how to embed into & serve static assets from your compiled binary.

First, the entire code for this example:

// main.go
package main

import (
	"bytes"
	"io"
	"net/http"
)

//go:generate go-bindata -pkg main -o bindata.go frontend/

func main() {
	http.ListenAndServe(":3000", http.StripPrefix("/", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
		if bs, err := Asset(req.URL.Path); err != nil {
			rw.WriteHeader(http.StatusNotFound)
		} else {
			var reader = bytes.NewBuffer(bs)
			io.Copy(rw, reader)
		}
	})))
}

// frontend/index.html
<html><body>Hello, <b>World</b>!</body></html>

To embed & serve the content of the frontend directory all you need to do is this:

$ go generate
$ go build

This leaves you with a compiled binary which includes all assets from the frontend directory - assuming you’ve got go-bindata installed. In our simple example just the index.html.

You can verify that this works using cURL:

$ curl "http://127.0.0.1:3000/frontend/index.html"
<html><body>Hello, <b>World</b>!</body></html>

How does this work, exactly? You might have seen the comment reading go:generate - that’s a hint for the use of code generation, added in go 1.4.

When running go generate comments like this are parsed and the given command is executed. In our case go-bindata -pkg main -o bindata.go frontend/

go-bindata is a binary that generates a new .go file, containing the bytes of all specified files, making them accessible by name. That’s where the previously undefined function Asset comes from.

One can easily imagine using this approach to generate self-contained binaries which serve an entire JavaScript single page application - without the need for any dependencies being installed on the deployment target.

Of course nothing stops you from embedding other files inside your binary, too - like SQL migration scripts, build-time environment informations, other binaries…

Having self contained binaries helps with a bunch of problems related to deployment, testing and provisioning - and you basically get this for free with go, without tedious management of make- or rakefiles.

While there are plenty of more good things to say about go I’ll leave it at this - it’s a great language, and you should hack with it sometime in the future, too.

Happy Hacking!

© Raphael Randschau 2010 - 2022 | Impressum