Go in WebAssembly
Go was early to the WebAssembly game, with the Go compiler producing wasm32
output alongside its regularly supported build targets.
But the core Go tools have fallen behind.
Instead, the alterative Go implementation called TinyGo seems to have taken the lead.
Available Implementations
- Go supports browser-based WebAssembly
- TinyGo supports
wasm32-wasi
as a build target - The Elements compiler may also support compiling browser-oriented Wasm
We have had the best luck with TinyGo.
Usage
With TinyGo, it is possible to compile most Go code into Wasm with WASI support. That means you can write Go code targeting the Fermyon Platform.
Pros and Cons
Things we like:
- TinyGo works very well
- Spin has full support for Go
We’re neutral about:
- The resulting binary sizes start at around 300k, but can rapidly climb
Things we’re not big fans of:
- Upstream (mainline) Go does not have WASI support
- TinyGo is still missing (mostly reflection-based) features of Go
Example
All of our examples follow a documented pattern using common tools.
TinyGo can compile to wasm32-wasi
, and TinyGo apps can be run on the Fermyon Platform. You will need to install TinyGo to do this example. Note that you also have to have Go installed
As with any Go project, the first step is to create a go.mod
file:
module github.com/fermyon/example-go
go 1.23
Since Spin has a Go SDK which is nice and easy to use, we’ll fetch that and use it:
$ go get github.com/fermyon/spin-go-sdk
Next, create a simple Go program named main.go
:
package main
import (
"fmt"
"net/http"
spinhttp "github.com/fermyon/spin-go-sdk/http"
)
func main() {
spinhttp.Handle(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello, World!")
})
}
When it comes to compiling, though, we will need to use TinyGo instead of Go. This particular set of flags has produced the best results for us:
tinygo build -target=wasi -gc=leaking -o main.wasm main.go
The above will output a main.wasm
file. A simple spin.toml
for running the above in Spin looks like this:
spin_manifest_version = 2
[application]
name = "spin-go-hello"
version = "1.0.0"
description = "Hello world app."
authors = ["Fermyon Engineering <engineering@fermyon.com>"]
[[trigger.http]]
component = "hello"
route = "/"
[component.hello]
source = "main.wasm"
[component.hello.build]
command = "tinygo build -target=wasi -gc=leaking -o main.wasm main.go"
Note: we’ve set the
hello
component’s build command to be the tinygo invocation above, so thatspin build
will run it from now on
From there, it’s just a matter of using spin up
to start the server. As usual, we can test using a web browser or Curl:
$ curl localhost:3000/
Hello, World!
Learn More
Here are some great resources:
- TinyGo has a step-by-step walkthrough for building and running Go WebAssembly modules
- There are instructions and examples
- Get started quickly with Spin templates for Go: e.g.
spin templates list --tag go
andspin new -t http-go
- A short article on compiling to Go’s “JS/Wasm” target