govapicore

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 16, 2026 License: Apache-2.0 Imports: 8 Imported by: 0

README

go-govapi-core

CI Go Reference Go Report Card License Latest Release

The shared HTTP-client kernel for a family of small Go clients to public/government APIs — go-interpol-client, go-factbook-client, go-wof-client, go-wikidata-client. Zero third-party dependencies.

Install

go get github.com/olehmushka/go-govapi-core

Usage

client := govapicore.NewHTTPClient(10 * time.Second)
ua := govapicore.ResolveUserAgent("MYAPP_USER_AGENT", "myapp/1.0 (contact@example.com)")

var out MyResponseType
err := govapicore.GetJSON(ctx, client, url, map[string]string{"User-Agent": ua}, 0, &out)
if err != nil {
    var statusErr *govapicore.ErrUnexpectedStatus
    if errors.As(err, &statusErr) {
        // the upstream API itself returned a non-200 — statusErr.Status / .Body
    }
    // otherwise: a network error or a response this client couldn't decode
}

Why this exists

Each of the client packages above talks to a different upstream API but was hand-rolling the exact same three things: a bounded *http.Client, an env-overridable User-Agent (several public APIs — Wikidata's SPARQL endpoint among them — 403 a request with no identifying UA), and a GET-JSON-with-a-size-cap-and-status-check helper. This package extracts just that shared shape — no more — from go-oikumenea's internal/hermenea/fetcher package, which had the identical pattern copy-pasted across five separate fetchers before this repo existed.

It is deliberately not a general-purpose HTTP framework: no retries, no caching, no batch/versioning concepts. A caller-supplied *http.Client and context.Context still govern the real transport behavior; this package only removes the boilerplate around them.

License

Apache 2.0 — see LICENSE.

Documentation

Overview

Package govapicore is the shared HTTP-client kernel for a family of small Go clients to public/government APIs (go-interpol-client, go-factbook-client, go-wof-client, go-wikidata-client, ...). It exists because each of those hand-rolled the same *http.Client construction, User-Agent resolution, and bounded JSON-GET boilerplate — extracted from go-oikumenea's internal/hermenea/fetcher package, which had the identical pattern copy-pasted across five separate fetchers.

Deliberately minimal: only what those clients actually share today. Batch-import concerns like source-version/checksum staging stay in hermenea, and streaming a large compressed download to disk (needed only by the WOF gazetteer client) stays in go-wof-client rather than living here unused by everyone else.

Index

Constants

View Source
const DefaultMaxBody int64 = 16 << 20

DefaultMaxBody bounds a GetJSON response body (16 MiB) when maxBytes <= 0, so a runaway or hostile endpoint can't exhaust memory.

Variables

This section is empty.

Functions

func GetJSON

func GetJSON(ctx context.Context, client *http.Client, url string, headers map[string]string, maxBytes int64, out any) error

GetJSON issues a GET request to url with the given headers, reads at most maxBytes of the response body (maxBytes <= 0 uses DefaultMaxBody), and decodes it as JSON into out. A non-200 response is reported as *ErrUnexpectedStatus, not a decode error, so a caller can distinguish "upstream is down" from "upstream sent something this client doesn't understand".

func NewHTTPClient

func NewHTTPClient(timeout time.Duration) *http.Client

NewHTTPClient returns an *http.Client bounded by timeout. timeout <= 0 means unbounded — the caller's context.Context deadline governs instead. This mirrors go-oikumenea's hermenea fetchers: sources that stream large payloads (a planet-scale WOF distribution, the Glottolog CLDF values.csv) intentionally use no fixed client deadline and rely on a job-level context instead, while a small point-lookup client wants a real bound.

func ResolveUserAgent

func ResolveUserAgent(envVar, fallback string) string

ResolveUserAgent returns the value of envVar if set and non-blank, else fallback. Several public APIs — the Wikidata SPARQL endpoint, iso639-3.sil.org — 403 a request carrying no identifying User-Agent or the bare Go default ("Go-http-client"), so a caller-identifying default is required, not optional; an operator embedding one of these clients in their own product can override it with their own contact via envVar without a code change.

Types

type ErrUnexpectedStatus

type ErrUnexpectedStatus struct {
	URL    string
	Status string
	Body   []byte
}

ErrUnexpectedStatus is returned by GetJSON when the response status is not 200 OK. Body is truncated to a small preview so an error message can't itself become an unbounded read.

func (*ErrUnexpectedStatus) Error

func (e *ErrUnexpectedStatus) Error() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL