Building on Minimal three tier Golang app, I made a three tier app with a Go frontend (as Web Assembly), a Go backend and a .txt as exemplary “data store”. Still no external dependencies, all compiling to a single binary. You need Go, Make and a browser. Compile and start with make, then open http://localhost:8000 and hit the button.
$ cat Makefile
all: hellofront hello
hellofront: hellofront.go
GOOS=js GOARCH=wasm go build hellofront.go structures.go
hello:
go run hello.go structures.go
$ cat structures.go
package main
type Answer struct {
Current int
}
$ cat hellofront.go
package main
import (
"encoding/json"
"fmt"
"net/http"
"syscall/js"
)
var current js.Value
func main() {
done := make(chan struct{}, 0)
current = js.Global().Get("document").Call("getElementById", "current")
js.Global().Set("GetCurrent", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
go GetCurrent()
return nil
}))
<-done
}
func GetCurrent() {
resp, err := http.Get("/current")
if err != nil {
fmt.Println(err)
}
var res Answer
err = json.NewDecoder(resp.Body).Decode(&res)
if err != nil {
fmt.Println(err)
}
current.Set("innerText", res.Current)
}
$ cat hello.go
package main
import (
_ "embed"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
//go:embed hellofront
var wasm []byte
// copied from $(go env GOROOT)/misc/wasm/wasm_exec.js
//go:embed wasm_exec.js
var wasm_exec string
var html = `<html>
<head>
<script>` + wasm_exec + `</script>
<script>
const go = new Go();
var module = WebAssembly.instantiateStreaming(fetch("hellofront"), go.importObject).then((result) => {
go.run(result.instance);
});
</script>
</head>
<body>
<h1 id="current"></h1>
<button onclick="GetCurrent()">Load Current</button>
</body>
</html>
`
const storefile = "myfancydatastore.txt"
func readFromStore() Answer {
b, err := ioutil.ReadFile(storefile)
if err != nil {
// do something
}
var a Answer
err = json.Unmarshal(b, &a)
if err != nil {
// do something
}
return a
}
func writeToStore(a Answer) {
b, err := json.Marshal(a)
if err != nil {
// do something
}
ioutil.WriteFile(storefile, b, 0644)
}
func serveIndex(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodGet {
w.Header().Set("content-type", "text/html; charset=utf-8")
w.Write([]byte(html))
}
}
func handler(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodGet {
w.Header().Set("content-type", "application/json")
a := readFromStore()
b, err := json.Marshal(a)
if err != nil {
// do something
}
w.Write(b)
a.Current = a.Current + 1
writeToStore(a)
}
}
func loadWASM(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodGet {
w.Write(wasm)
}
}
func main() {
writeToStore(Answer{Current: 0})
mux := http.NewServeMux()
mux.HandleFunc("/", serveIndex)
mux.HandleFunc("/current", handler)
mux.HandleFunc("/hellofront", loadWASM)
fmt.Println("Starting server on localhost:8000")
http.ListenAndServe("localhost:8000", mux)
}
Posted in
programming
2023-03-03 14:41 UTC