Full Example
Complete gateway setup with routes, discovery, load balancing, circuit breakers, and dashboard.
This guide demonstrates all key Bastion features working together in a single, runnable Go program: static routes, FARP auto-discovery, load balancing, circuit breakers, rate limiting, health checks, the admin dashboard, hooks, and OpenAPI aggregation.
Complete Gateway
package main
import (
"fmt"
"log"
"net/http"
"time"
"github.com/xraph/forge"
"github.com/xraph/bastion"
"github.com/xraph/bastion/extension"
"github.com/xraph/forge/extensions/discovery"
dashext "github.com/xraph/forge/extensions/dashboard"
"github.com/xraph/vessel"
)
func main() {
app := forge.New()
// ─── 1. Register discovery for FARP auto-discovery ──────
app.Register(discovery.New())
// ─── 2. Register the Forge dashboard ────────────────────
app.Register(dashext.New())
// ─── 3. Register Bastion API gateway ────────────────────
app.Register(extension.New(
// Base path for all proxied routes
bastion.WithBasePath("/api"),
// Static routes for known services
bastion.WithRoute(bastion.RouteConfig{
Path: "/users",
ServiceName: "user-service",
Targets: []bastion.TargetConfig{
{URL: "http://user-service-1:8080", Weight: 3},
{URL: "http://user-service-2:8080", Weight: 2},
{URL: "http://user-service-3:8080", Weight: 1},
},
StripPrefix: true,
Enabled: true,
OpenAPISpec: "http://user-service-1:8080/openapi.json",
}),
bastion.WithRoute(bastion.RouteConfig{
Path: "/orders",
ServiceName: "order-service",
Targets: []bastion.TargetConfig{
{URL: "http://order-service:8080", Weight: 1},
},
StripPrefix: true,
Enabled: true,
OpenAPISpec: "http://order-service:8080/openapi.json",
}),
// FARP discovery -- auto-detect new services on the network
bastion.WithDiscoveryEnabled(true),
bastion.WithDiscoveryPollInterval(15 * time.Second),
bastion.WithDiscoveryWatchMode(true),
bastion.WithDiscoveryAutoPrefix(true),
bastion.WithDiscoveryPrefixTemplate("/{{.ServiceName}}"),
bastion.WithDiscoveryStripPrefix(true),
// Load balancing -- weighted round-robin across targets
bastion.WithLoadBalancing(bastion.LoadBalancingConfig{
Strategy: bastion.LBWeightedRoundRobin,
}),
// Circuit breakers -- fail fast on unhealthy upstreams
bastion.WithCircuitBreaker(bastion.CircuitBreakerConfig{
Enabled: true,
FailureThreshold: 5,
FailureWindow: 60 * time.Second,
ResetTimeout: 30 * time.Second,
HalfOpenMax: 3,
}),
// Rate limiting -- global request throttling
bastion.WithRateLimiting(bastion.RateLimitConfig{
Enabled: true,
RequestsPerSec: 1000,
Burst: 200,
PerClient: true,
}),
// Health checks -- active probing of upstream targets
bastion.WithHealthCheck(bastion.HealthCheckConfig{
Enabled: true,
Interval: 10 * time.Second,
Timeout: 5 * time.Second,
Path: "/_/health",
FailureThreshold: 3,
SuccessThreshold: 2,
EnablePassive: true,
PassiveFailThreshold: 5,
}),
// Retry -- automatic retry for transient failures
bastion.WithRetry(bastion.RetryConfig{
Enabled: true,
MaxAttempts: 3,
Backoff: bastion.BackoffExponential,
InitialDelay: 100 * time.Millisecond,
MaxDelay: 5 * time.Second,
Multiplier: 2.0,
Jitter: true,
RetryableStatus: []int{502, 503, 504},
RetryableMethods: []string{"GET", "HEAD", "OPTIONS"},
}),
// OpenAPI aggregation -- merge specs from all upstreams
bastion.WithOpenAPI(bastion.OpenAPIConfig{
Enabled: true,
Path: "/openapi.json",
UIPath: "/swagger",
Title: "My API Gateway",
Description: "Unified API documentation",
Version: "1.0.0",
RefreshInterval: 30 * time.Second,
FetchTimeout: 10 * time.Second,
MergeStrategy: "prefix",
EnableRootDocs: true,
}),
// Admin dashboard
bastion.WithDashboard(bastion.DashboardConfig{
Enabled: true,
BasePath: "/gateway",
Title: "API Gateway Dashboard",
Realtime: true,
}),
// CORS
bastion.WithCORS(bastion.CORSConfig{
Enabled: true,
AllowOrigins: []string{"https://app.example.com"},
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "PATCH"},
AllowHeaders: []string{"Authorization", "Content-Type"},
MaxAge: 86400,
}),
))
// ─── 4. Register hooks for logging and alerting ─────────
app.AfterStart(func() {
gw, err := vessel.Inject[*bastion.Gateway](app.Container())
if err != nil {
log.Fatal("resolve bastion:", err)
}
// Log every proxied request
gw.Hooks().OnRequest(func(r *http.Request, route *bastion.Route) error {
log.Printf("[REQ] %s %s -> route=%s service=%s",
r.Method, r.URL.Path, route.ID, route.ServiceName)
return nil
})
// Tag responses with gateway identity
gw.Hooks().OnResponse(func(resp *http.Response, route *bastion.Route) {
resp.Header.Set("X-Served-By", "bastion")
resp.Header.Set("X-Route-ID", route.ID)
})
// Log upstream errors
gw.Hooks().OnError(func(err error, route *bastion.Route, w http.ResponseWriter) {
log.Printf("[ERR] route=%s error=%v", route.Path, err)
})
// Alert on health changes
gw.Hooks().OnUpstreamHealth(func(event bastion.UpstreamHealthEvent) {
if !event.Healthy {
log.Printf("[ALERT] Upstream %s (%s) is DOWN", event.TargetID, event.TargetURL)
} else {
log.Printf("[INFO] Upstream %s (%s) recovered", event.TargetID, event.TargetURL)
}
})
// Log circuit breaker transitions
gw.Hooks().OnCircuitBreak(func(targetID string, from, to bastion.CircuitState) {
log.Printf("[CIRCUIT] %s: %s -> %s", targetID, from, to)
})
// Log route table changes (FARP discoveries, manual additions)
gw.Hooks().OnRouteChange(func(event bastion.RouteEvent) {
log.Printf("[ROUTE] %s: %s (source=%s)",
event.Type, event.Route.Path, event.Route.Source)
})
// Print summary
stats := gw.Snapshot()
fmt.Printf("\nGateway started with %d routes and %d upstreams\n",
stats.TotalRoutes, stats.TotalUpstreams)
fmt.Printf("Dashboard: http://localhost:8080/gateway\n")
fmt.Printf("API Docs: http://localhost:8080/gateway/openapi.json\n")
fmt.Printf("Swagger: http://localhost:8080/gateway/swagger\n\n")
})
// ─── 5. Run the application ─────────────────────────────
app.Run()
}YAML Config Equivalent
The same gateway configuration expressed as YAML:
# config.yaml
app:
name: api-gateway
port: 8080
bastion:
enabled: true
base_path: "/api"
routes:
- path: /users
service_name: user-service
targets:
- url: http://user-service-1:8080
weight: 3
- url: http://user-service-2:8080
weight: 2
- url: http://user-service-3:8080
weight: 1
strip_prefix: true
enabled: true
openapi_spec: http://user-service-1:8080/openapi.json
- path: /orders
service_name: order-service
targets:
- url: http://order-service:8080
weight: 1
strip_prefix: true
enabled: true
openapi_spec: http://order-service:8080/openapi.json
discovery:
enabled: true
poll_interval: 15s
watch_mode: true
auto_prefix: true
prefix_template: "/{{.ServiceName}}"
strip_prefix: true
load_balancing:
strategy: weightedRoundRobin
circuit_breaker:
enabled: true
failure_threshold: 5
failure_window: 60s
reset_timeout: 30s
half_open_max: 3
rate_limiting:
enabled: true
requests_per_sec: 1000
burst: 200
per_client: true
health_check:
enabled: true
interval: 10s
timeout: 5s
path: "/_/health"
failure_threshold: 3
success_threshold: 2
enable_passive: true
passive_fail_threshold: 5
retry:
enabled: true
max_attempts: 3
backoff: exponential
initial_delay: 100ms
max_delay: 5s
multiplier: 2.0
jitter: true
retryable_status: [502, 503, 504]
retryable_methods: [GET, HEAD, OPTIONS]
openapi:
enabled: true
path: /openapi.json
ui_path: /swagger
title: "My API Gateway"
description: "Unified API documentation"
version: "1.0.0"
refresh_interval: 30s
fetch_timeout: 10s
merge_strategy: prefix
enable_root_docs: true
dashboard:
enabled: true
base_path: /gateway
title: "API Gateway Dashboard"
realtime: true
cors:
enabled: true
allow_origins: ["https://app.example.com"]
allow_methods: [GET, POST, PUT, DELETE, PATCH]
allow_headers: [Authorization, Content-Type]
max_age: 86400What This Example Demonstrates
| Feature | What Is Configured |
|---|---|
| Static Routes | Two services (user-service, order-service) with explicit targets and weights |
| FARP Discovery | Auto-detection of new services on the network with watch mode |
| Load Balancing | Weighted round-robin distributes traffic by target weight |
| Circuit Breakers | Auto-trip after 5 failures in 60s, half-open probe after 30s |
| Rate Limiting | 1000 req/s global with per-client tracking and burst of 200 |
| Health Checks | Active probing every 10s, passive failure detection |
| Retry | Exponential backoff with jitter for 502/503/504 errors |
| OpenAPI Aggregation | Merged spec from all upstream services at /openapi.json |
| Admin Dashboard | Real-time dashboard at /gateway with WebSocket updates |
| CORS | Cross-origin support for the frontend application |
| Hooks | Request logging, response tagging, error alerting, health alerts |
Running the Gateway
# Build and run
go run main.go
# Or with a custom config file
CONFIG_FILE=config.yaml go run main.goOnce running:
- Proxy endpoint:
http://localhost:8080/api/*-- routes to upstream services - Dashboard:
http://localhost:8080/gateway-- real-time admin UI - API docs:
http://localhost:8080/gateway/openapi.json-- aggregated OpenAPI spec - Swagger UI:
http://localhost:8080/gateway/swagger-- interactive API explorer - Admin API:
http://localhost:8080/gateway/api/routes-- route management - Stats:
http://localhost:8080/gateway/api/stats-- gateway statistics - WebSocket:
ws://localhost:8080/gateway/ws-- real-time event stream
Testing the Gateway
# List all routes
curl http://localhost:8080/gateway/api/routes | jq
# Proxy a request through the gateway
curl http://localhost:8080/api/users
# Check gateway statistics
curl http://localhost:8080/gateway/api/stats | jq
# View discovered services
curl http://localhost:8080/gateway/api/discovery/services | jq
# Create a route via the admin API
curl -X POST http://localhost:8080/gateway/api/routes \
-H "Content-Type: application/json" \
-d '{
"path": "/products",
"targets": [{"url": "http://product-service:8080", "weight": 1}],
"stripPrefix": true,
"enabled": true,
"protocol": "http"
}'Next Steps
- Forge Extension -- Extension lifecycle and DI integration
- Hook System -- Custom request/response processing
- OpenAPI Aggregation -- Unified API documentation
- Admin REST API -- Complete API endpoint reference
- Admin Dashboard -- Real-time monitoring UI