gob fetcher for wasm example

This commit is contained in:
2026-07-13 02:24:06 -04:00
parent b02df48a66
commit 93e36c9abc
13 changed files with 339 additions and 6 deletions

66
go/httputil/fetch.go Normal file
View File

@@ -0,0 +1,66 @@
package httputil
import (
"bytes"
"encoding/gob"
"encoding/json"
"errors"
)
// Client-side fetch helpers — the counterparts to RespondGob / RespondJSON. In a
// gowasm app the server and the WebAssembly client share the same Go types, so
// the server encodes with RespondGob(data) and the client decodes straight back
// into that type with FetchGob — no JSON, no hand-written unmarshalling. These
// are neutral (they only touch encoding/*), so app code can call them while
// staying SSR-able; the actual HTTP transport is client-only and injected at
// startup by the wasm runtime (SetClientTransport), the inversion vdom.Schedule
// uses.
// clientTransport does an HTTP GET returning the raw body; nil on the server.
var clientTransport func(url string) ([]byte, error)
// SetClientTransport installs the HTTP GET used by FetchGob / FetchJSON. The
// wasm client runtime calls this at startup with wasmruntime.FetchBytes.
func SetClientTransport(get func(url string) ([]byte, error)) { clientTransport = get }
func fetchInto[T any](url string, cb func(T, error), decode func([]byte, *T) error) {
go func() {
var out T
if clientTransport == nil {
cb(out, errors.New("httputil: no client transport installed (client-only)"))
return
}
body, err := clientTransport(url)
if err != nil {
cb(out, err)
return
}
if err := decode(body, &out); err != nil {
cb(out, err)
return
}
cb(out, nil)
}()
}
// FetchGob GETs url and gob-decodes the body into T (pair with RespondGob), then
// calls cb(result, err) on a background goroutine — safe to call from a render
// or event handler; set the result into a signal in the callback. T is inferred
// from the callback:
//
// httputil.FetchGob("/api/quotes", func(qs []Quote, err error) {
// if err != nil { /* … */ } else { quotes.Set(qs) }
// })
func FetchGob[T any](url string, cb func(T, error)) {
fetchInto(url, cb, func(b []byte, out *T) error {
return gob.NewDecoder(bytes.NewReader(b)).Decode(out)
})
}
// FetchJSON GETs url and json-decodes the body into T — for third-party JSON
// APIs (or your own RespondJSON). Same callback/goroutine shape as FetchGob.
func FetchJSON[T any](url string, cb func(T, error)) {
fetchInto(url, cb, func(b []byte, out *T) error {
return json.Unmarshal(b, out)
})
}