~/ / portfolio / go-utils
# on this page

$ whoami go-utils

go-utils

Go utility library for AWS microservices — structured logging with trace correlation, typed errors, a resilient HTTP client, RS256 JWT, Postgres/Redis/S3 adapters and SQS/SNS/EventBridge messaging. Observability-first, config-from-env, interface-driven.

v2.0.0 · 2026-07-06 Go 1.25 AWS SDK v2 OpenTelemetry MIT

$ cat ~/principles.md #

01

Observability-first

Every package emits OpenTelemetry spans, and the logger injects trace_id/span_id from the context into every line — logs and traces correlate out of the box.

02

Config from env

Every adapter has ConfigFromEnv(prefix). Same shape everywhere — DB_*, REDIS_*, SQS_* — so wiring a service is boilerplate-free and 12-factor by default.

03

Interface-driven

Each package exposes an interface with an unexported implementation. Depend on the interface, mock it in tests, swap the backend without touching callers.

$ cat ~/install.md #

// v2+ carries the /v2 suffix (Go semantic import versioning)
$ go get github.com/juanMaAV92/go-utils/v2

// import a package
import "github.com/juanMaAV92/go-utils/v2/logger"

# Public module — no auth needed. Requires Go 1.25+. AWS clients use the standard credential chain (env, shared config, IAM role); no hardcoded credentials.

$ go list ./... 18 pkgs #

logger Structured JSON#

Flat JSON to stdout on top of slog. When a span is active, trace_id and span_id are injected automatically — no manual plumbing.

// Create once at startup
log := logger.New("payments-api", env.GetEnvironment())

// context-first — trace_id/span_id are pulled from the active span
log.Info(ctx, "process_payment", "payment captured",
    "amount", 50000, "currency", "COP")

Output:

{"time":"2026-03-22T10:15:30.12Z","level":"INFO","service":"payments-api","step":"process_payment","message":"payment captured","trace_id":"4bf9...","span_id":"00f0...","amount":50000,"currency":"COP"}

errors Typed + Echo#

A single ErrorResponse type with HTTP code, error code and messages. Predefined constructors for every status, plus a drop-in Echo handler.

// Typed, predefined errors
return errors.ErrNotFound()
return errors.ErrBadRequest().WithMessage("email is required")

// Fully custom
return errors.New(http.StatusConflict, "CONFLICT", []string{"already exists"})

// Wire the Echo handler once — maps *ErrorResponse to the right status + JSON
e.HTTPErrorHandler = echoerr.HTTPErrorHandler

httpclient Resilient client#

Resty-based client with retries, timeouts and W3C trace propagation. Request/response bodies are never logged — only metadata — so credentials and tokens don't leak.

c := httpclient.New(log,
    httpclient.WithBaseURL("https://api.example.com"),
    httpclient.WithRetryCount(3),
    httpclient.WithServiceName("billing"),
)

// W3C trace context is injected automatically; bodies are never logged
resp, err := c.Post(ctx, "/invoices", invoice)
var out Invoice
_ = resp.JSON(&out)

security/jwt RS256#

Generic RS256 signing and validation over any claims struct. Validation enforces RS256 only, requires exp, and checks the issuer — closing the classic algorithm-substitution and never-expiring-token gaps.

svc, _ := jwt.New(privatePEM, publicPEM, "auth-service")

// Sign
claims := MyClaims{RegisteredClaims: svc.RegisteredClaims(time.Hour), UserID: "u_1"}
token, _ := svc.GenerateToken(&claims)

// Validate — RS256 only, exp required, issuer checked
got, err := jwt.ValidateToken[MyClaims](svc, token)

data stores postgres · redis · s3#

Interface-driven persistence adapters, each with ConfigFromEnv and OTel tracing.

  • database/postgresql — GORM wrapper: Create, Find/FindMany with pagination, UpdateWhere, WithTransaction; pg error codes mapped to sentinels (cause preserved for errors.Is)
  • cache/redis — typed Set/Get/GetOrSet, sets, pub/sub, atomic GETDEL, TTL/NX/XX options
  • storage/s3Get/Put/Head/Delete plus presigned upload/download URLs

messaging sqs · sns · scheduler#

Event-driven building blocks with trace context flowing through message attributes.

  • sqs — batch producer + worker-pool consumer that transparently unwraps the SNS envelope; backoff on poll errors and a graceful drain on shutdown (no duplicate delivery)
  • sns — producer that propagates W3C trace context via message attributes
  • scheduler — one-time EventBridge schedules with a webhook-via-Lambda helper and a Disabled flag that survives updates

middleware/identity RBAC#

Echo middleware that reads a gateway-injected identity (user, roles, permissions) from request headers into the context.

  • HasPermission / HasAnyPermission / RequireCapability with all:all superadmin
  • Wildcard patterns (orders:*) and role checks
  • ToRequestOptions forwards the identity downstream — scoped forwarding no longer leaks superadmin past the pattern

core helpers env · validator · telemetry · …#

The small, dependency-light pieces every service reuses.

  • env — typed env parsing (GetEnvAsInt/Duration/Bool/Slice, MustHave)
  • validator — struct validation with field-aware messages, backed by go-playground
  • telemetry — one-call OTLP setup; parent-based sampling that honors upstream decisions
  • pointers · timeutil — generics and date helpers
  • testutil/echo · testutil/http — request builders and JSON assertions for handler tests

$ cat ~/ecosystem.md #

Blueprint Production Echo microservice template built on go-utils: go-echo-blueprint ↗
Built on aws-sdk-go-v2, gorm, go-redis, resty, go-playground/validator, golang-jwt, opentelemetry-go
Framework HTTP helpers target Echo; everything else (logging, messaging, stores, jwt) is framework-agnostic