The Go library for building WebAssembly components with componentize-go
A Bytecode Alliance projectModule go.bytecodealliance.org/pkg is the Go library for Wasm components. It adapts standard-library interfaces (net/http, log/slog) to standard wasi:* interfaces and ships the committed bindings and WIT worlds needed to build HTTP components with componentize-go. The use of this package significantly reduces the number of files generated and committed for a typical go application.
The library targets two worlds defined in wit/world.wit:
bytecodealliance:pkg/wasip2(default): a sync WASI P2 component exportingwasi:http/incoming-handler@0.2.8, buildable with stock Go.bytecodealliance:pkg/wasip3(opt-in): an async WASI P3 component exportingwasi:http/handler@0.3.0with streaming bodies and native concurrency.
| Package | Description |
|---|---|
wasihttp |
net/http adapter for wasi:http: serve incoming requests with a standard http.Handler and send outbound requests through an http.RoundTripper. One API, two implementations selected by build tag (see below). |
wasilog |
slog.Handler implementation over wasi:logging. |
wasiconfig |
Helpers over wasi:config/store. |
wit/types, wit/runtime, wit/async |
Core WIT value types (option, result, tuple, stream, future) and the canonical-ABI runtime support used by generated bindings. |
imports/... |
Committed generated bindings for the wasi:* interfaces imported by the two worlds (both the 0.2.8 and 0.3.0 families). |
exports/... |
Per-world generated //go:wasmexport glue and export trampolines. |
Bindings under imports/ and exports/ are generated by
regenerate_bindings.sh — do not edit them.
The wasi:* WIT packages under wit/deps/ are vendored verbatim from the WebAssembly package registry using wkg. To update a dependency:
-
Bump its version in
fetch_wit_deps.shand inwit/world.wit. -
Re-fetch the vendored WIT:
$ ./fetch_wit_deps.sh -
Regenerate the committed bindings and commit everything together:
$ ./regenerate_bindings.sh
Note: the script uses
wkg getwith exact versions rather thanwkg wit fetchbecause the library intentionally depends on two versions of several packages (e.g.wasi:http@0.2.8andwasi:http@0.3.0), andwkg wit fetchresolves at most one version per package name.
wasihttp compiles to one of two implementations; the exported API is identical under both:
- Default (no tag): sync WASI P2 (
wasi:http@0.2.8). Matches thebytecodealliance:pkg/wasip2world. -tags componentizego_async: async WASI P3 (wasi:http@0.3.0) with streaming bodies and native concurrency. Matches thebytecodealliance:pkg/wasip3world.
componentize-go sets the tag automatically when building an async world.
package main
import (
"net/http"
"go.bytecodealliance.org/pkg/wasihttp"
)
func init() {
wasihttp.HandleFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, component!"))
})
}
func main() {}Add componentize-go as a Go tool and build:
$ go get -tool github.com/bytecodealliance/componentize-go
$ go tool componentize-go buildThe default world (bytecodealliance:pkg/wasip2@0.1.0) is declared in componentize-go.toml and discovered automatically. To build the async WASI P3 world instead:
$ go tool componentize-go -w bytecodealliance:pkg/wasip3 buildPure-Go conversion logic (header conversion and friends) has microbenchmarks that run on the host:
$ go test -bench=. -benchmem ./...Packages that call wasi:* imports only link on wasm targets, so the benchmarks live in host-compilable packages (e.g. internal/httpconv).
For A/B comparisons use benchstat: collect ≥10 samples per side with the test filter disabled, then compare:
$ go test -bench=. -benchmem -count=10 -run='^$' ./... > old.txt
$ # ... apply your change ...
$ go test -bench=. -benchmem -count=10 -run='^$' ./... > new.txt
$ benchstat old.txt new.txtAsk over in the Bytecode Alliance Zulip.
See CONTRIBUTING.md for more information about contributing to this repository.