Ship Go services faster, safely.
hwm-go-utils is the shared toolkit behind HiWay Media's Go services: a CRUD REST API from a single struct, Keycloak JWT auth that fails closed, and clients for Keycloak, Nomad, NATS, MySQL and Redis — hardened, tested, and ready for production.
go get github.com/HiWay-Media/hwm-go-utils@latest
Get started Browse packages GitHub
Why hwm-go-utils
A REST API in one line
SetEndpoints[Product] wires list, get, create and delete routes on Fiber + GORM — paginated, validated, injection-safe.
Auth that fails closed
Keycloak JWT verification with an RS-only allow-list, mandatory expiry, optional issuer/audience and role checks — no panics on odd tokens.
Clients that survive outages
NATS reconnects forever, the Keycloak admin token refreshes itself, the Nomad client works with any base URL and ACL token.
Secure by default
No database errors or secrets in responses and logs, server-owned fields protected, dependencies kept free of known CVEs.
Tested without infrastructure
Every fix ships with a regression test: GORM dry-runs, fake HTTP servers and signed test tokens — go test -race in CI.
Everyday helpers
Generic slice and map helpers, date maths, URL encoding, conversions and file utilities — small, dependency-free, documented.
From zero to a protected REST API
One struct, one database, one line per resource:
package main
import (
"os"
"github.com/HiWay-Media/hwm-go-utils/api/generic"
"github.com/HiWay-Media/hwm-go-utils/api/middlewares"
"github.com/HiWay-Media/hwm-go-utils/db"
"github.com/HiWay-Media/hwm-go-utils/log"
"github.com/gofiber/fiber/v2"
)
type Product struct {
ID uint `json:"id" gorm:"primaryKey"`
Name string `json:"name" validate:"nonzero"`
Price float64 `json:"price"`
}
func main() {
logger := log.GetLogger("info")
database, err := db.Open("app", "secret", "mysql", 3306, "shop", "5", "20")
if err != nil {
logger.Fatalf("database: %v", err) // the error never contains the password
}
app := fiber.New()
// realm public key from Keycloak → Realm settings → Keys → RS256 → Public key
api := app.Group("/api", middlewares.JwtProtected(os.Getenv("KEYCLOAK_PUBLIC_KEY"),
middlewares.WithIssuer("https://sso.example.com/realms/shop"),
))
// GET /api/products, GET /api/products/:id, POST /api/products, DELETE /api/products/:id
generic.SetEndpoints[Product]("products", api, database, logger)
logger.Fatal(app.Listen(":8080"))
}
Walk through it step by step →
How it fits together
flowchart LR
S(["Your Go service"]) --> L
subgraph L["hwm-go-utils"]
direction TB
API["api/* · CRUD · JWT · client"]
KC["keycloak"]
NO["nomad"]
NA["nats_helper"]
DB["db"]
RD["redis · keydb"]
LG["log · utils"]
end
API --> MySQL
API -. public key .-> Keycloak
DB --> MySQL[("MySQL")]
KC --> Keycloak(["Keycloak"])
NO --> Nomad(["Nomad API"])
NA --> NATS(["NATS · JetStream"])
RD --> Redis[("Redis · KeyDB")]
Packages
| Package | What you get | Guide |
|---|---|---|
api/generic |
Generic CRUD handlers, service and store for any GORM model | Generic CRUD |
api/middlewares |
JwtProtected, RoleCheck, safe claim getters |
JWT middleware |
api/client |
Minimal JSON REST client with bearer auth | HTTP client |
api/models |
Standard OK / KO response envelopes |
Responses |
keycloak |
Login, tokens, users, groups, realms, client roles | Keycloak |
nomad |
Job definitions, allocations, scale, restart, run, delete | Nomad |
nats_helper |
Resilient NATS connection and JetStream context | NATS |
db |
MySQL via GORM with pool limits | Database |
redis, keydb |
Cluster and single-node Redis clients | Redis & KeyDB |
log |
zap console logger configured from a string | Logging |
utils/* |
Strings, slices, maps, ints, dates, conversions, files | Utilities |
Requires Go 1.26 or later. Upgrading from v0.6.x? Read the upgrade guide first — a few defaults changed for the better.