Die meiste Zeit entwickle ich HTTP-Backends, die ich mit curl oder Postman teste. Von Zeit zu Zeit habe ich den Wunsch nach einer einfachen UI, i.d.R. weil die API dann besser vorführbar ist. Ich habe diverse Male den State-of-the-Art Weg eingeschlagen, mir das notwendige React, Vue oder Angular-Wissen angelesen und eine mehr oder weniger schöne Web-UI gebaut. Das hat meistens gut funktioniert, aber fast immer einen komischen Beigeschmack hinterlassen. Zum einen fühlte ich mich in JS-basierten Umgebungen häufig unsicher und musste viel nachschlagen oder mir Beispiel-Code anschauen. Und zum anderen fand ich das Setup und Deployment der jeweiligen Umgebungen immer einigermassen komplex und wenig zugänglich.
Um Rahmen unserer DataFrog-Entwicklung bin ich einen anderen Weg gegangen und habe die Web-UI für die API in Go entwickelt. Das ganze hat für meine Zwecke erstaunlich gut funktioniert. Während der Entwicklung sind eine Reihe von Funktionen entstanden, die ich als so nützlich empfand, dass ich sie in eigenständiges Modul SimpleWeb extrahiert und veröffentlicht habe.
Mit SimpleWeb lassen sich mit wenigen Go-Zeilen einfache, serverseitig gerenderte Web-Anwendungen bauen. Das folgende Beispiel startet einen Web-Server auf Port 3030, der eine dynamisch gerenderte Index-Seite ausliefert:
func main() {
simpleweb.Register("/", func(w http.ResponseWriter, r *http.Request) {
simpleweb.Render("templates/index.html", w, struct {
Name string
}{Name: "SimpleWeb"})
}, "GET")
simpleweb.Run()
}
Das zugehörige HTML-Template enthält den Platzhalter .Name für den einzufügenden Namen:
<html lang="en">
<body>
<h1>Hello, {{.Name}}</h1>
</body>
</html>
Die HTML-Templates der Anwendung werden über die go:embed-Directive statisch ins Go-Binary eingebettet, so dass der Web-Server aus einer einzigen Binärdatei besteht, die keinerlei Abhängigkeiten auf der Zielmaschine hat.
Abschließend noch ein kurzer Hinweis zum Thema Cross-Compilation: Ich entwickle unter MacOS, deploye aber i.d.R. in eine Linux-Umgebung. Dem Go-Compiler wird über die Parameter GOOS und GOARCH mitgeteilt, für welche Zielplattform er übersetzen soll. So erzeugt der Aufruf
env GOOS=linux GOARCH=amd64 go build -o hello main.go
das Binary hello, das ich einfach per scp auf meinen Linux-Server kopieren und dort starten kann.