Build Go JSON/WebSocket services with tagged logging, auto-wired APIs, and test-first ergonomics.
- Tagged JSON logs keep every request’s story without coupling handlers to a logger. Attach tags with
ctx.WithTag, andctx/logtakes care of consistently emitting them. - The
http,api, andtestpackages form a feedback loop: define a Go struct once, expose it over HTTP/WebSocket, and exercise it in your tests without binding boilerplate. - Batteries stay optional. Supporting packages like
enc,shutdown, andutils/promplug in when you need them without locking you into a monolith.
- Install the module:
go get github.com/ohait/forego@latest
- Wire an API, HTTP server, and graceful shutdown:
package main import ( "regexp" fctx "github.com/ohait/forego/ctx" flog "github.com/ohait/forego/ctx/log" fhttp "github.com/ohait/forego/http" "github.com/ohait/forego/shutdown" ) type WordFilter struct { Blacklist *regexp.Regexp In string `api:"in,required"` Out string `api:"out"` } func (wf *WordFilter) Do(c fctx.C) error { wf.Out = wf.Blacklist.ReplaceAllString(wf.In, "***") return nil } func main() { c, cf := fctx.Background() defer cf(nil) c = fctx.WithTag(c, "service", "wordfilter") srv := fhttp.NewServer(c) srv.MustRegisterAPI(c, "/api/wordfilter/v1", &WordFilter{ Blacklist: regexp.MustCompile(`(foo|bar)`), }) addr, err := srv.Listen(c, "127.0.0.1:8080") if err != nil { panic(err) } flog.Infof(c, "listening on %s", addr.String()) shutdown.WaitForSignal(c, cf) }
- Call the endpoint; the OpenAPI definition is already available at
/openapi.json:curl -s -X POST localhost:8080/api/wordfilter/v1 \ -H 'content-type: application/json' \ -d '{"in":"foo and friends"}'
- Test the same struct without HTTP glue:
import ( "regexp" "testing" "github.com/ohait/forego/api" "github.com/ohait/forego/test" ) func TestWordFilter(t *testing.T) { c := test.Context(t) out := api.Test(c, &WordFilter{ Blacklist: regexp.MustCompile(`(foo|bar)`), In: "foo and bar", }) test.EqualsStr(t, "*** and ***", out.Out) }
ctx/ctx/log: wrapcontext.Contextso tags, rich errors, and JSON logs travel together; handlers don’t have to know who consumes the logs.http,api,http/ws: map structs to REST and WebSocket endpoints, emit OpenAPI, and reuse the same types in business logic and tests.test: AST-aware assertions that reuse your API structs directly, producing readable success and failure output.enc: an intermediate JSON representation that keeps parsing efficient when you need custom coercion.shutdown,utils/prom,storage: supporting utilities for graceful shutdown, lightweight metrics, and simple storage patterns.
c = fctx.WithTag(c, "tracking", "a1b2c3")
flog.Infof(c, "completed request")Produces a JSONL entry similar to:
{"level":"info","message":"completed request","tags":{"service":"wordfilter","tracking":"a1b2c3"}}
Every handler in forego/http automatically enriches tags with user agent, path, remote address, and exposes the same metadata to your own log calls.
- api — bind Go structs to RPC-style operations, generate OpenAPI, and provide helpers to test them directly.
- enc — flexible JSON intermediate forms (
enc.Node,enc.Map,enc.Pairs) that avoidjson.RawMessagegymnastics. - http — production-grade HTTP server with automatic request tagging, gzip, OpenAPI serving, and API registration.
- http/ws — WebSocket RPC bindings that reuse the same struct/tag approach as REST.
- ctx — context helpers, tagged metadata, structured logging, and rich error wrappers.
- shutdown — graceful signal handling with hold/release coordination.
- test — expressive assertions powered by source analysis.
- utils/prom — lightweight Prometheus-compatible metrics.
- utils — shared helpers (AST, caches, lists, sync) used across packages.
- Use it when you’re building Go JSON or WebSocket services and want consistent logging, automatic documentation, and tests that stay close to your business logic.
- Wait if you need a polished, stable framework or non-Go integrations—this project is still evolving and APIs may move.
Further details live in the package READMEs linked above.