The Go counterpart to dexpace/java-sdk
and dexpace/python-sdk. It is an
HTTP-client toolkit, not an HTTP client: it supplies the request/response
plumbing — a composable pipeline of policies (retry, auth, logging, …) — that
sits between application code and a transport.
The Go port leans on the standard library. Requests and responses are
net/http types, the transport seam is satisfied by *http.Client, and bodies
are plain io.Reader/io.ReadCloser. There is no reinvented HTTP model and no
Okio-style I/O layer; the toolkit adds the composition seam on top of
net/http, the way azcore
and the AWS SDK for Go do.
client := dexpace.New(
dexpace.WithRetry(retry.Options{MaxRetries: 3}),
dexpace.WithCredential(cred, "https://api.example.com/.default"),
dexpace.WithLogging(nil), // nil → slog.Default()
)
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.example.com/v1/things", nil)
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if rerr := httperr.FromResponse(resp); rerr != nil {
return rerr // *httperr.ResponseError for any 4xx/5xx
}Layered bottom-up; each layer is importable on its own and depends only on the standard library.
| Package | Responsibility |
|---|---|
pipeline |
The Policy / Transporter contract and the Pipeline that runs an ordered policy chain over an *http.Request. Request.Next advances the chain; Request.RewindBody replays the body for retries. |
transport |
The default net/http-backed Transporter that terminates a pipeline. |
retry |
Retry policy — exponential backoff with full jitter, Retry-After, body rewind. |
idempotency |
Idempotency-key policy (default-on for POST). |
auth |
TokenCredential contract and BearerTokenPolicy (HTTPS-only, cached). |
logging |
Structured request/response logging via log/slog, with URL redaction. |
instrumentation |
Vendor-neutral Tracer/Meter SPIs with no-op defaults, plus tracing and metrics policies. |
redact |
Default-deny URL redactor (strips userinfo, redacts query values) shared by logs, traces, and errors. |
httperr |
ResponseError for non-success responses; buffers and rewinds the body. |
mediatype |
Immutable media-type value with parsing and common constants. |
header |
Canonical HTTP header-name constants. |
pagination |
Generic pagination as iter.Seq2 range-over-func iterators — cursor/token, page-number, and RFC 8288 Link-header strategies, with a WithMaxPages cap. |
conditions |
Conditional- and range-request value types (ETag, Range, Conditions). |
config |
Layered override → environment → default settings resolver; non-failing typed getters. |
serde |
Serialization seam (Marshaler/Unmarshaler) with a JSON default, plus Tristate for PATCH payloads. |
sse |
Server-Sent Events (text/event-stream) WHATWG parser + reconnecting Stream (Last-Event-ID replay); Client.EventStream runs it through the pipeline. |
jsonl |
JSON Lines / NDJSON streaming decoder (iter.Seq2). |
webhook |
Inbound webhook signature verification (constant-time HMAC + timestamp tolerance). |
formdata |
Multipart/form-data request body builder (replayable; file uploads). |
root dexpace |
Umbrella Client wiring the default policy stack. |
dexpace.New assembles policies outermost-first:
[errors] → client-identity → idempotency → retry → auth → [date] → [tracing] → [metrics] → logging → custom → transport
Retry wraps the inner policies, so auth re-runs (and may refresh its token) on
every attempt and logging records each attempt. Build a custom order directly
with pipeline.New(transport, policies...) when you need something else.
An Idempotency-Key is sent on POST requests by default (disable with
WithoutIdempotency); WithDate is opt-in.
By default Client.Do follows net/http semantics, where a non-2xx status is
not an error. WithErrors opts into the typed error model: Client.Do then
returns a *httperr.ResponseError for non-2xx responses and a
*httperr.TransportError for transport failures. It is off by default.
Tracing and metrics are opt-in and route through the instrumentation
package's vendor-neutral SPIs (no-op by default, so nothing is emitted until you
wire a backend):
WithTracing(tracer)— installs a tracing policy that emits a span per request via the instrumentationTracerSPI and injects a W3Ctraceparentheader.WithMetrics(meter)— installs a metrics policy recording request duration and in-flight requests via the instrumentationMeterSPI.WithRedactionAllowlist(params...)— preserves the listed query-param values in redacted URLs (logs and traces); all other query values are redacted by default.
URLs are redacted by default across logs, traces, and errors: userinfo is
stripped and query values are redacted unless allowlisted with
WithRedactionAllowlist.
WithCredential(cred, scopes...)— authenticates requests with bearer tokens from aTokenCredential(HTTPS-only, cached).WithTokenCache(cache)— shares a bearer-token cache (anauth.TokenCache, in-memory by default) across clients so a cached token is reused.WithBasicAuth(username, password)— authenticates requests with HTTP Basic auth (HTTPS-only).WithAPIKey(header, key)— sets an API-key header on every request (HTTPS-only).WithDigestAuth(username, password)— authenticates requests with HTTP Digest Access Authentication, RFC 7616 (MD5/SHA-256, qop=auth; HTTPS-only).WithConfig(cfg)— sources defaults fromDEXPACE_*environment variables —DEXPACE_USER_AGENT,DEXPACE_MAX_RETRIES(0 or negative disables retries),DEXPACE_RETRY_BASE_DELAY,DEXPACE_HTTP_TIMEOUT(default transport only) — for settings not set explicitly; explicit options always win.
Client.EventStream(ctx, req, opts...) returns an iter.Seq2[sse.Event, error]
reconnecting Server-Sent Events stream that runs through the pipeline — every
connection clones req, sets Accept: text/event-stream, and replays the
Last-Event-ID after a mid-stream interruption, so auth, logging, and tracing
run per connection. A non-2xx response or a transport failure on connect ends
the stream with that error; cancel the request context to stop.
req, _ := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.example.com/v1/events", nil)
for ev, err := range client.EventStream(ctx, req) {
if err != nil {
log.Printf("stream ended: %v", err)
break
}
fmt.Println(ev.Data)
}Go 1.26+. The module targets modern idioms: generics, range-over-func
iterators (iter.Seq2), math/rand/v2, log/slog, and the min/max
builtins.
make check # tidy + fmt + vet + lint + test (race + coverage)
make test # go test -race -covermode=atomic ./...
make lint # golangci-lintThe SDK ships zero third-party runtime dependencies; only the standard
library is imported by non-test code. See CONTRIBUTING.md
for conventions and CLAUDE.md for the enforced rules.
See CHANGELOG.md for release notes.
MIT — see LICENSE.