UP | HOME

Joschka Tillmanns

Building Web Applications In Pure Go

Motivation

Nowadays it seems that a language is really only complete once it has it's own JavaScript transpiler. Go is one of my favorite programming languages. Not only for it's simplicity, readability and toolset but for it's absolute outstanding standard library. Of course Go is no exception for the JavaScript transpiler rule. To transpile Go into JavaScript Gopherjs (written by Richard Musiol) is your go to solution. While I thought some time about writing an application using Gopherjs I never had the right problem that could be addressed using Gopherjs.

Recently I had to deal with a lot of MAIL attachments that exceed the recommended size which should be send per mail. I thought about a public upload solution that would allow any sender to easily post me files of (seemingly) arbitrary size. As having a public upload service could cause a lot of security problems (attackers could exploit the service and spam my hard disk) I needed some sort of authentication. Though I didn't want to force users of the service to enter a username and password. Therefore I developed tokenshare. Using tokenshare I can simply create a token that is referenced by a unique ID. Now I can simply share this token with any user which then may may silently login to the system.

This sort of problem seemed like the right problem to be approached by Gopherjs. Using Gopherjs I am able to write the whole application in pure Go. Even further Gopherjs is able to present the user with severally additional user written libraries that provide Go bindings to popular JavaScript libraries. However I only utilized Dominik Honnef's HTML DOM bindings to minimize code size and strengthen type safety.

The beauty of Gopherjs is that I can use standard Go code to implement the client logic. While this sounds obvious the implications are quiet amazing: I am able to easily write unit tests for the server which utilizes the same code the client uses. This means that I can implement the server, write the client implementations and test everything within a single set of unit test without even thinking about the DOM or JavaScript. Afterwards the only thing left is writing a thin layer around that client implementation that deals with DOM/HTML API interaction.

Furthermore I may use the same types the server uses without even thinking about changes to the type model. A single go build for the whole project informs me about a possible code part that relies on parts of the model I just changed. I don't have to rely on undependable string-replacements inside the JavaScript code.

Concept

The applications splits of into two user interfaces:

  1. an admin page that presents options to create tokens, lists all available tokens and respective download links and presents the user with the link for each token.
  2. an upload page that provides the end user with the ability to upload the actual file.

Upload%20Process.png

Figure 1: Sequence Diagram of Application Lifecycle

All access to the admin page is authenticated using basic HTTP authentication. All requests done by the upload page are restricted to the token ID passed along side to the user. Hence I can create a token while authenticated by Basic HTTP Auth and send the token ID to any user which then may upload the file in question. The idea is that I can now easily create a token per email contact and simply send it withing my email signature. That way if a contact needs to send me a larger file he can use the token I provided for him.

Implementation

As previously stated I started doing this by simply writing the server and respective database access while simultaneously writing unit tests. Hence I created to Go packages for the server tokenshare as well as tokenshare/backend. The client implementation that I needed to write the unit tests are defined in the tokenshare package while server and unit test implementation are obviously defined in tokenshare/backend. I am now able to easily create a thin layer on top of the client implementation already created that deals with DOM interaction.

Whats especially great about Goppherjs is that I still get to use goroutines and channels to do asynchronous calls to the server. Therefore I am able to define a function which uploaded the provided data and sends the transfer progress on a channel.

func Transfer(call, name, id string, data []byte, progress chan int) error {}

Reading this channel I can push the process updates to the dom and give the user feedback about the upload status:

func (c Client) upload(data []byte, name, id string, progress func(int)) error {
	pr := make(chan int)
	defer close(pr)

	go func() {
		for i := range pr {
			progress(i)
		}
	}()

	return Transfer(ReqTransfer, name, id, data, pr)
}

Having the power of Go's concurrency patterns is a big advantage and made the implementation of the client quite simple and straightforward. The Client type implements several methods that implement communications with the server while updating the status to DOM elements. For instance the previously used progress function just consists of:

func (c Client) progress(total int, div *dom.HTMLDivElement) func(int) {
	return func(p int) {

		div.SetInnerHTML(fmt.Sprintf("Progress: %d%%",
			int(float64(p)/float64(total)*100)))
	}
}

The DOM-bindings package allow a clean and easy way to interact with the user interface. Based on the methods defined on the Client type the packages tokenshare/app as well as tokenshare/upload are defined. These packages are compiled using the Gopherjs utility and provide the functionality for the Admin and the User.

Conclusion

Programming in Go over JavaScript provides a lot of advantages, type safety, code sharing, or access to most of the Go standard library. One set of unit tests cover the client and server code. Thus the client implementation leaves less room for error. Additionally not having to switch constantly between two languages makes a big different.

I have to also mention that having the complete Go toolset available for the client implementation, like go vet, errcheck, gofmt and so forth is pretty amazing. This allows me to write clean code in a fast pace without dealing with Javascript characteristics.

The code is available at github.com/jostillmanns/tokenshare.