Skip to content

x/gateway Primer

Beta — API is stable within a minor version. Check Release Posture and Extension Maturity before adopting in production.

Open this page after x/* Family when the change is clearly about edge transport behavior rather than the smallest canonical service shape: proxying upstream requests, rewriting paths, balancing across backends, or wiring gateway-level health.

x/gateway is the canonical and only app-facing entrypoint for gateway and edge transport work in Plumego. Its subordinate packages are reached through x/gateway first, not opened directly:

  • x/gateway/cache — gateway-edge HTTP response caching
  • x/gateway/discovery — service instance resolution
  • x/gateway/ipc — inter-process transport
  • x/gateway/protocol — protocol adapter registry (gRPC, GraphQL transcoding)
  • x/gateway/protocolmw — protocol-aware middleware for the proxy pipeline
  • x/gateway/transform — request/response header, query-param, and body transformation chains

Gateway and edge transport behavior should be isolated behind application-local configuration when compatibility matters. Check x/gateway/module.yaml, Release Posture, and Extension Maturity before adopting proxy, rewrite, balancer, discovery, or IPC APIs in production.

  • you are building or modifying reverse proxy behavior (proxy.go)
  • you are configuring gateway routing rules or path rewrites (rewrite.go)
  • you are adding or tuning a load-balancing strategy (balancer.go)
  • you are wiring a cache layer at the gateway edge (cache/)
  • you are implementing gateway-level health checks (health.go)
  • you are adding discovery-based backend resolution or IPC transport helpers
  • you are adapting gRPC or GraphQL upstream protocols via the proxy pipeline (protocol/, protocolmw/)
  • you are composing header, query-param, or body transforms at the gateway edge (transform/)
  • the change is about reusable CRUD resource APIs — that belongs in x/rest
  • the work introduces tenant-specific edge policy — coordinate with x/tenant
  • the change is core bootstrap or stable root entrypoints
  • the work is business-specific gateway policy baked into the extension primitives

First files to read in the current repository

Section titled “First files to read in the current repository”
  1. x/gateway/module.yaml
  2. x/gateway/entrypoints.go
  3. x/gateway/backend.go
  4. x/gateway/balancer.go
  5. x/gateway/rewrite.go (if path rewriting is involved)
  6. x/gateway/cache/ (if caching is involved)
Keep it in x/gateway when the work is aboutMove out when the work becomes
proxy: reverse-proxy handler construction, upstream selection, request forwardingreusable resource-interface CRUD transport — that belongs in x/rest
rewrite: path rewriting, header injection, and modifier compositionbusiness-specific gateway policy that belongs in application code
balancer: backend pool management and load-balancing strategy selectiontenant-specific routing policy or per-tenant backend assignment
cache: gateway-edge response caching adaptersapplication-level data caching not at the edge — use x/data/cache instead
health: gateway-level backend health tracking and circuit statefine-grained component health types in the stable health module
protocol: protocol adapter registry, gRPC and GraphQL transcoding at the proxy edgebusiness-level protocol logic — keep that in application code
protocolmw: protocol-aware middleware composing adapters into the proxy pipelinegeneric HTTP middleware not tied to protocol adaptation — use middleware/
transform: header injection/removal, query-param edits, JSON body rewriting, method/status overridesapplication-level response shaping outside the gateway edge
discovery / ipc via x/gateway entrypointsopening x/gateway/discovery or x/gateway/ipc directly for work that belongs at the gateway surface
import "github.com/spcent/plumego/x/gateway"
proxy := gateway.NewGateway(gateway.GatewayConfig{
Targets: []string{
"http://backend-1:8080",
"http://backend-2:8080",
},
LoadBalancer: gateway.NewRoundRobinBalancer(),
})
// As a standalone handler
r.Handle("/api/*", proxy)
// Or as middleware
r.Handle("/api/*", proxy.Middleware()(nextHandler))
proxy, err := gateway.RegisterProxy(router, "/api/*", gateway.GatewayConfig{
Targets: []string{"http://upstream:9000"},
LoadBalancer: gateway.NewRoundRobinBalancer(),
})
ConstructorStrategy
NewRoundRobinBalancer()Rotate through backends in order
NewRandomBalancer()Pick a backend at random
NewWeightedRoundRobinBalancer()Round-robin weighted by backend weight
NewIPHashBalancer()Route same client IP to same backend
NewLeastConnectionsBalancer()Send to backend with fewest active connections
proxy := gateway.NewGateway(gateway.GatewayConfig{
Targets: []string{"http://a:8080", "http://b:8080"},
LoadBalancer: gateway.NewLeastConnectionsBalancer(),
})
proxy := gateway.NewGateway(gateway.GatewayConfig{
Targets: []string{"http://upstream:9000"},
PathRewrite: gateway.StripPrefix("/api/v1"),
})

Available rewrite helpers:

FunctionEffect
StripPrefix(prefix)Remove prefix from path before forwarding
AddPrefix(prefix)Prepend prefix to forwarded path
ReplacePrefix(old, new)Swap one prefix for another
RewriteMap(rules)Apply first matching exact-path rule
Chain(rewriters...)Compose multiple rewriters left-to-right
pool, err := gateway.NewBackendPool([]string{
"http://backend-1:8080",
"http://backend-2:8080",
})
if err != nil {
log.Fatal(err)
}
checker := gateway.NewHealthChecker(pool, &gateway.HealthCheckConfig{
Interval: 10 * time.Second,
Timeout: 2 * time.Second,
Path: "/healthz",
})
checker.Start()
defer checker.Stop()

Gateway and edge transport is where the confusion between “routing” (which belongs in router) and “proxying” (which belongs here) causes the most boundary drift. x/gateway is the gatekeeper for all six subordinate packages: always start here before opening cache, discovery, ipc, protocol, protocolmw, or transform directly. That indirection keeps the app-facing surface stable even as subordinate internals evolve.