Gateway

Features

Gateway extension capabilities

Multi-Protocol Proxying

The gateway transparently proxies traffic across four protocols using a single entry point. Protocol detection is automatic based on request headers:

ProtocolDetectionImplementation
HTTPDefaulthttputil.ReverseProxy with custom Director
WebSocketUpgrade: websocket headergobwas/ws bidirectional frame copying
SSEAccept: text/event-stream headerStreaming proxy with http.Flusher
gRPCContent-Type: application/grpcHTTP/2 transport with trailer propagation

For each request, the proxy engine:

  1. Checks global rate limits.
  2. Matches the request to a route via RouteManager.
  3. Checks per-route rate limits.
  4. Authenticates the request (if route auth is configured).
  5. Checks the response cache (if route caching is enabled).
  6. Runs OnRequest hooks.
  7. Selects a target via the load balancer.
  8. Checks the circuit breaker for the selected target.
  9. Dispatches to the appropriate protocol handler.

Route Management

Routes map incoming request paths to upstream targets with full lifecycle management:

ext := app.Container().MustResolve("gateway").(*gateway.Extension)
rm := ext.RouteManager()

// Add a route programmatically
rm.AddRoute(&gateway.Route{
    ID:       "users-api",
    Path:     "/api/users",
    Methods:  []string{"GET", "POST", "PUT", "DELETE"},
    Targets:  []*gateway.Target{
        {URL: "http://users-svc:8080", Weight: 1},
        {URL: "http://users-svc:8081", Weight: 1},
    },
    Protocol:    gateway.ProtocolHTTP,
    Source:      gateway.SourceManual,
    StripPrefix: true,
    Enabled:     true,
})

// Update an existing route
rm.UpdateRoute("users-api", updatedRoute)

// Remove a route
rm.RemoveRoute("users-api")

// Enable/disable routes
rm.EnableRoute("users-api")
rm.DisableRoute("users-api")

Routes can be created from three sources:

  • manual -- configured statically in YAML/code or via the admin REST API.
  • farp -- auto-generated from FARP service discovery schemas (OpenAPI, AsyncAPI, GraphQL).
  • discovery -- auto-generated from service discovery backends.

Load Balancing

Five load balancing strategies distribute traffic across upstream targets:

StrategyBehavior
roundRobinCycles through targets in order
weightedRoundRobinRespects target Weight values
randomRandom target selection
leastConnectionsRoutes to the target with fewest active connections
consistentHashHashes a configurable header for sticky routing
gateway.NewExtension(
    gateway.WithLoadBalancing(gateway.LoadBalancingConfig{
        Strategy:      gateway.StrategyLeastConnections,
        ConsistentKey: "X-User-ID", // For consistentHash strategy
    }),
)

Each target tracks active connections, total requests, total errors, and latency percentiles (AvgLatencyMs, P99LatencyMs) for informed load balancing decisions.

Circuit Breakers

Per-target three-state circuit breakers prevent cascade failures:

Closed ──(failures exceed threshold)──→ Open ──(reset timeout)──→ Half-Open
  ↑                                                                    │
  └──────────(requests succeed)────────────────────────────────────────┘
gateway.NewExtension(
    gateway.WithCircuitBreaker(gateway.CircuitBreakerConfig{
        Enabled:          true,
        FailureThreshold: 5,              // Open after 5 failures
        FailureWindow:    30 * time.Second, // Within this window
        ResetTimeout:     60 * time.Second, // Time before half-open
        HalfOpenMax:      2,              // Max requests in half-open state
    }),
)

The circuit breaker manager provides per-target state:

cbm := ext.CircuitBreakerManager()
cb := cbm.Get("target-id")
state := cb.State() // CircuitClosed, CircuitOpen, CircuitHalfOpen

State change hooks notify your application when circuit breakers trip:

ext.Hooks().OnCircuitBreak(func(targetID string, from, to gateway.CircuitState) {
    log.Printf("circuit breaker: %s changed from %s to %s", targetID, from, to)
})

Health Monitoring

Active and passive health monitoring for upstream targets:

Active probes send HTTP requests to a configurable health endpoint at regular intervals:

gateway.NewExtension(
    gateway.WithHealthCheck(gateway.HealthCheckConfig{
        Enabled:              true,
        Interval:             10 * time.Second,
        Timeout:              5 * time.Second,
        Path:                 "/health",
        FailureThreshold:     3, // Mark unhealthy after 3 failures
        SuccessThreshold:     2, // Mark healthy after 2 successes
        EnablePassive:        true,
        PassiveFailThreshold: 5,
    }),
)

Passive monitoring tracks proxy errors as implicit health signals. Targets with too many consecutive errors are automatically marked unhealthy.

Health change events are available via hooks:

ext.Hooks().OnUpstreamHealth(func(event gateway.UpstreamHealthEvent) {
    log.Printf("target %s: healthy=%v", event.TargetID, event.Healthy)
})

Rate Limiting

Token-bucket rate limiting at three levels:

// Global rate limit
gateway.WithRateLimit(gateway.RateLimitConfig{
    Enabled:        true,
    RequestsPerSec: 1000,
    Burst:          100,
})

// Per-route rate limit (in RouteConfig)
route.RateLimit = &gateway.RateLimitConfig{
    Enabled:        true,
    RequestsPerSec: 50,
    Burst:          10,
    PerClient:      true,       // Per-IP limiting
    KeyHeader:      "X-API-Key", // Or per-API-key
}

When rate limited, the gateway returns 429 Too Many Requests with Retry-After header.

Retry with Backoff

Configurable retry policies with three backoff strategies:

route.Retry = &gateway.RetryConfig{
    Enabled:          true,
    MaxAttempts:      3,
    Backoff:          gateway.BackoffExponential,
    InitialDelay:     100 * time.Millisecond,
    MaxDelay:         5 * time.Second,
    Multiplier:       2.0,
    Jitter:           true,
    RetryableStatus:  []int{502, 503, 504},
    RetryableMethods: []string{"GET", "HEAD"},
    BudgetPercent:    20, // Max 20% of requests can be retries
}
StrategyBehavior
exponentialDelay doubles each attempt (with optional jitter)
linearDelay increases by InitialDelay each attempt
fixedConstant delay between attempts

Traffic Splitting

Advanced traffic routing for canary deployments, A/B testing, blue-green deployments, and shadow traffic:

route.TrafficPolicy = &gateway.TrafficPolicy{
    Type: gateway.TrafficCanary,
    Rules: []gateway.TrafficRule{
        {
            Match: gateway.TrafficMatch{
                Type:  gateway.MatchHeader,
                Key:   "X-Canary",
                Value: "true",
            },
            Weight:  100,
            Targets: []string{"canary-target-id"},
        },
    },
}
Split TypeUse Case
canaryRoute matching requests to canary targets
blueGreenSwitch all traffic between two target sets
abTestSplit traffic based on headers/cookies
mirrorCopy traffic to shadow targets (fire-and-forget)
weightedPercentage-based traffic distribution

Authentication

The gateway integrates with the Forge auth extension and provides built-in auth providers:

// Register built-in providers
ext.Auth().RegisterProvider(gateway.NewAPIKeyAuthProvider(
    "apikey", "X-API-Key", "api_key",
    []*gateway.APIKeyEntry{
        {Key: "prod-key-123", Subject: "service-a", Scopes: []string{"read", "write"}},
    },
))

ext.Auth().RegisterProvider(gateway.NewBearerTokenAuthProvider(
    "jwt",
    func(ctx context.Context, token string) (*gateway.GatewayAuthContext, error) {
        // Validate JWT and return context
        return &gateway.GatewayAuthContext{
            Subject: "user-123",
            Scopes:  []string{"admin"},
        }, nil
    },
))

// Per-route auth configuration
route.Auth = &gateway.RouteAuthConfig{
    Enabled:   true,
    Providers: []string{"jwt", "apikey"},
    Scopes:    []string{"read"},
}

Authenticated requests get forwarded auth headers (X-Auth-Subject, X-Auth-Provider, X-Auth-Scopes) to upstream services.

Request Transformation

Modify requests and responses as they pass through the gateway:

  • Path rewriting -- StripPrefix removes the matched prefix; AddPrefix prepends a new prefix; RewritePath replaces the path entirely.
  • Header manipulation -- Add, set, or remove headers on requests and responses via HeaderPolicy and TransformConfig.
route := &gateway.Route{
    Path:        "/v2/api/*",
    StripPrefix: true,
    AddPrefix:   "/internal",
    Headers: gateway.HeaderPolicy{
        Add: map[string]string{"X-Gateway": "forge"},
        Set: map[string]string{"X-Version": "2"},
        Remove: []string{"X-Internal-Only"},
    },
}

Hook System

Extensible lifecycle hooks for custom logic at every stage:

hooks := ext.Hooks()

// Before request is proxied
hooks.OnRequest(func(r *http.Request, route *gateway.Route) error {
    r.Header.Set("X-Request-ID", uuid.New().String())
    return nil // return error to abort the request
})

// After response is received
hooks.OnResponse(func(resp *http.Response, route *gateway.Route) {
    log.Printf("response: %d for %s", resp.StatusCode, route.Path)
})

// On proxy error
hooks.OnError(func(err error, route *gateway.Route, w http.ResponseWriter) {
    log.Printf("proxy error for %s: %v", route.Path, err)
})

// On route changes (add, update, delete)
hooks.OnRouteChange(func(event gateway.RouteEvent) {
    log.Printf("route %s: %s", event.Action, event.Route.Path)
})

Response Caching

In-memory response caching with per-route policies:

route.Cache = &gateway.RouteCacheConfig{
    Enabled: true,
    TTL:     5 * time.Minute,
    Methods: []string{"GET"},
}

The cache respects Cache-Control headers and supports custom cache key generation.

OpenAPI Aggregation

The gateway aggregates OpenAPI specifications from all upstream services into a unified specification, served at the gateway with an optional Swagger UI.

aggregator := ext.OpenAPI()
// OpenAPI spec is available at the gateway dashboard

Admin Dashboard and REST API

The gateway serves a ForgeUI-based dashboard with real-time WebSocket updates showing routes, upstreams, stats, and discovered services.

Admin REST API (default prefix: /gateway/api):

EndpointDescription
GET /api/routesList all routes (filter by source, protocol)
POST /api/routesCreate a manual route
PUT /api/routes/:idUpdate a route
DELETE /api/routes/:idDelete a route
POST /api/routes/:id/enableEnable a route
POST /api/routes/:id/disableDisable a route
GET /api/upstreamsList targets with health status
GET /api/statsGateway statistics
GET /api/stats/routesPer-route statistics
GET /api/configCurrent configuration (sanitized)
GET /api/discovery/servicesDiscovered services
POST /api/discovery/refreshTrigger discovery refresh

Observability

  • Prometheus metrics -- request count, latency, error rate, circuit breaker state, upstream health.
  • Structured access logging -- method, path, status, latency, target, protocol.
  • OpenTelemetry trace propagation -- forwards trace headers to upstream services.

TLS and Mutual TLS

Configure TLS for upstream connections with optional client certificates:

target.TLS = &gateway.TargetTLSConfig{
    CACert:     "/path/to/ca.pem",
    ClientCert: "/path/to/client.pem",
    ClientKey:  "/path/to/client-key.pem",
    SkipVerify: false,
}

How is this guide?

On this page