Writing Handlers
Reactor model
Section titled “Reactor model”GoMode uses a reactor model: main() runs once to initialize the Go runtime, then handle_zerobuf() is called for each request. The WASM instance stays alive.
func main() { // Initialize anything global here. // This runs once when WASM is first loaded.}
//export handle_zerobuffunc handleZerobuf(reqBase uint32) uint32 { // Called for every request. // reqBase points to zerobuf tagged values in WASM memory.}Request format
Section titled “Request format”The Worker writes request fields as zerobuf tagged value slots directly into WASM memory:
- Field 0:
method(string) atreqBase + 0*16 - Field 1:
path(string) atreqBase + 1*16
Read with the zerobuf helpers:
reqAddr := uintptr(reqBase)method := readZBString(reqAddr + 0*16)path := readZBString(reqAddr + 1*16)Response format
Section titled “Response format”Write response fields as zerobuf tagged values and return the pointer:
func writeResponse(status int32, contentType string, body string) uint32 { resp := make([]byte, 3*16) // 3 fields × 16 bytes respStrings = respStrings[:0] respBase = uintptr(unsafe.Pointer(&resp[0]))
writeZBI32(respBase+0*16, status) writeZBString(respBase+1*16, contentType) writeZBString(respBase+2*16, body)
return uint32(respBase)}Response fields:
- Field 0:
status(i32) atrespBase + 0*16 - Field 1:
contentType(string) atrespBase + 1*16 - Field 2:
body(string) atrespBase + 2*16
Calling Zig SIMD
Section titled “Calling Zig SIMD”Import the gomode package and call Zig functions directly:
import "gomode"
func handleSimd() uint32 { data := []float64{1, 2, 3, 4, 5, 6, 7, 8} ptr := uint32(uintptr(unsafe.Pointer(&data[0]))) count := uint32(len(data))
sum := gomode.ZigSimdSumF64(ptr, count) dot := gomode.ZigSimdDotF64(ptr, ptr, count)
gomode.ZigSimdScaleF64(ptr, count, 2.0)
return writeResponse(200, "text/plain", formatFloat(sum))}These compile to direct WASM call instructions — zero overhead.
Routing
Section titled “Routing”Route by path in your handler:
switch path {case "/": return writeResponse(200, "text/plain", "Hello!")case "/simd": return handleSimd()case "/json": return writeResponse(200, "application/json", `{"ok":true}`)default: return writeResponse(404, "text/plain", "not found")}GC considerations
Section titled “GC considerations”-gc=leaking— Never frees memory. Smallest binary (79KB). Fine for short-lived or low-traffic handlers.-gc=conservative— Actual garbage collection. Use for long-lived DO instances with many requests.
Build with:
tinygo build -target wasip1 -scheduler=none -gc=leaking \ -ldflags="-extldflags='build/zig-abi.o'" \ -o build/go.wasm .