Skip to Content
How-To GuidesGoOutgoing HTTP Requests from a Go Agent

Outgoing HTTP Requests from a Go Agent

Overview

Use the standard library net/http package directly. The SDK routes net/http through Golem’s durable wasi:http transport, so each request is recorded in the oplog and served from it on replay after a restart rather than re-issued — outbound calls are effectively exactly-once across crashes/restarts. No manual wiring or custom transport is needed.

Steps

  1. Import net/http (and io to read the body).
  2. Make the request — http.Get(url), or build an *http.Request and use http.DefaultClient.Do.
  3. Read the response body, then Close it.
  4. Do not add manual retry loops — Golem retries transient HTTP failures per the agent’s retry policy. A panic fails the agent (see golem-add-agent-go → Returning Failures).

Example

package impl import ( "io" "net/http" "os" "myapp/agents/fetcher" "github.com/golemcloud/golem/sdks/go/golem" ) type state struct{} var agent = fetcher.Agent.Implement(func(fetcher.ID) *state { return &state{} }) func init() { agent.Handle(fetcher.Fetch, func(_ *golem.Context[state], in fetcher.FetchIn) string { url := "https://api.example.com/thing?q=" + in.Query // golem.Must panics on error, which fails the agent. Use it for calls that // should not fail normally. resp := golem.Must(http.Get(url)) defer resp.Body.Close() return string(golem.Must(io.ReadAll(resp.Body))) }) }

golem.Must(v, err) returns v or panics on err — the fail-loud idiom for errors the agent cannot handle. To surface an HTTP failure to the caller as a value instead, return a golem.Result and inspect the error yourself (see golem-add-agent-go → Returning Failures).

POST / custom requests

req := golem.Must(http.NewRequest("POST", url, strings.NewReader(body))) req.Header.Set("Content-Type", "application/json") resp := golem.Must(http.DefaultClient.Do(req)) defer resp.Body.Close()

Calling Golem Agent HTTP Endpoints

When making HTTP requests to other Golem agent endpoints (or your own), ordinary structured body fields are decoded from a JSON object with the body field names as top-level keys. Include only fields not bound to path/query/header values. A single struct field named Payload needs {"payload": {"name": "Widget", "count": 5}}, not the bare inner object. Unstructured text and binary fields instead consume the raw body without this wrapper.

For example, given this endpoint definition:

type RecordIn struct{ Body string } var Record = Agent.Method[RecordIn, golem.Unit]("record", golem.HTTP(golem.POST("/record")))

The correct HTTP request sends a JSON object with a body field, not a raw text string:

// ✅ CORRECT — JSON object with the field name as the key req := golem.Must(http.NewRequest("POST", "http://my-app.localhost:9006/recorder/main/record", strings.NewReader(`{"body":"a"}`))) req.Header.Set("Content-Type", "application/json") // ❌ WRONG — a raw text body does not match Golem's JSON body mapping req = golem.Must(http.NewRequest("POST", "http://my-app.localhost:9006/recorder/main/record", strings.NewReader("a"))) req.Header.Set("Content-Type", "text/plain")

Rule of thumb: For structured Golem agent bodies, send application/json with the body field names as JSON keys. For unstructured text, send raw UTF-8 text with text/plain; for unstructured binary, send raw bytes with the appropriate MIME type. Load golem-http-params-go for the restrictions and the separate CLI positional/Go-literal rules; do not reuse the HTTP wrapper for CLI arguments.

Durability & retries

  • The HTTP call is a durable operation: on replay the recorded response is returned, so the external service is not hit again.
  • Do not write manual retry/backoff loops — Golem retries failed operations automatically (a default policy applies; customize with the retry-policy helpers only when the strategy must change).

Key Constraints

  • Target is WASM: there are no raw sockets. net.Dial, most database drivers, and custom transports won’t work — outgoing traffic goes through the SDK’s WASI-backed net/http transport.
  • Async handles cannot outlive an invocation: read/consume the response within the same handler; don’t stash an unresolved response/body in agent state to use later.
GuideDescription
golem-add-http-endpoint-goExpose this agent over HTTP (incoming requests)
golem-call-another-agent-goCall another Golem agent instead of an external HTTP service
golem-add-agent-goDefine the agent making the request