Skip to Content
Next — You're reading the unreleased preview docs. APIs and pages may change before the next release.Switch to the current docs →
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 wasi:http transport. For durable agents, completed request/response-body recordings replay from the oplog. Recovery can reissue a request whose response-body scope was incomplete, with the same Golem-managed idempotency key. Exactly-once external effects require the peer to deduplicate that key; recording alone does not guarantee them. 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 blanket manual retry loops — Golem retries eligible transient HTTP failures per the host retry policy. A panic fails the agent (see golem-add-agent-go → Returning Failures).

Example

package impl import ( "io" "net/http" "net/url" "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 { requestURL := "https://api.example.com/thing?q=" + url.QueryEscape(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(requestURL)) defer resp.Body.Close() if resp.StatusCode < 200 || resp.StatusCode >= 300 { panic("unexpected upstream HTTP status: " + resp.Status) } 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).

http.Get does not return an error for non-2xx status codes. Always check StatusCode according to the API contract. The example above treats unexpected statuses as agent failures. For intentional failures that callers should handle, declare fetcher.Fetch with output type golem.Result[string, string] and register a matching handler:

agent.Handle(fetcher.Fetch, func(_ *golem.Context[state], in fetcher.FetchIn) golem.Result[string, string] { requestURL := "https://api.example.com/thing?q=" + url.QueryEscape(in.Query) resp, err := http.Get(requestURL) if err != nil { return golem.Err[string, string]("upstream request failed") } defer resp.Body.Close() if resp.StatusCode < 200 || resp.StatusCode >= 300 { return golem.Err[string, string]("upstream HTTP status: " + resp.Status) } body, err := io.ReadAll(resp.Body) if err != nil { return golem.Err[string, string]("upstream response read failed") } return golem.Ok[string, string](string(body)) })

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

  • In durable mode, completed recordings replay without contacting the external service. An interrupted response-body scope can reissue the recorded request with the same idempotency key. For mutating requests, require peer-side deduplication before relying on exactly-once effects. Ephemeral invocations do not recover by replay.
  • Do not layer blanket retry/backoff loops over host retries. Only eligible failures and statuses are retried, within the configured policy; permanent failures can still reach your code. Customize with the retry-policy helpers when the strategy must change, and handle final failures explicitly.

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