Getting Started
Install Bastion and configure your first API gateway in under ten minutes.
This guide walks you through installing Bastion, defining routes, adding resilience features, and optionally enabling service discovery and the admin dashboard. By the end you will have a working API gateway proxying traffic to upstream services.
Prerequisites
- Go 1.24+
- A Go module (
go mod init) - A Forge application (
github.com/xraph/forge)
Install
go get github.com/xraph/bastionThis pulls in the root module and all sub-packages (proxy, resilience, health, discovery, security, etc.).
Step 1 -- Create a basic gateway
Register Bastion as a Forge extension with a single static route:
package main
import (
"github.com/xraph/forge"
"github.com/xraph/bastion"
)
func main() {
app := forge.NewApp(forge.AppConfig{
Name: "api-gateway",
Version: "1.0.0",
Extensions: []forge.Extension{
bastion.NewExtension(
bastion.WithRoute(bastion.RouteConfig{
Path: "/users/*",
Targets: []bastion.TargetConfig{
{URL: "http://user-service:8080", Weight: 1},
},
StripPrefix: true,
Protocol: bastion.ProtocolHTTP,
Enabled: true,
}),
),
},
})
app.Run()
}Requests to /users/* are proxied to http://user-service:8080 with the /users prefix stripped. A request to /users/42 becomes /42 on the upstream.
Step 2 -- Add multiple routes
Call WithRoute multiple times to define additional routes:
bastion.NewExtension(
bastion.WithRoute(bastion.RouteConfig{
Path: "/users/*",
Targets: []bastion.TargetConfig{
{URL: "http://user-service:8080", Weight: 1},
},
StripPrefix: true,
Protocol: bastion.ProtocolHTTP,
Enabled: true,
}),
bastion.WithRoute(bastion.RouteConfig{
Path: "/orders/*",
Targets: []bastion.TargetConfig{
{URL: "http://order-service:8080", Weight: 1},
},
StripPrefix: true,
Protocol: bastion.ProtocolHTTP,
Enabled: true,
}),
bastion.WithRoute(bastion.RouteConfig{
Path: "/notifications/*",
Targets: []bastion.TargetConfig{
{URL: "http://notification-service:8080", Weight: 1},
},
StripPrefix: true,
Protocol: bastion.ProtocolHTTP,
Enabled: true,
}),
)Step 3 -- Configure load balancing
Add multiple targets to a route and choose a load balancing strategy:
bastion.WithRoute(bastion.RouteConfig{
Path: "/users/*",
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,
Protocol: bastion.ProtocolHTTP,
LoadBalancer: bastion.LoadBalancerWeightedRoundRobin,
Enabled: true,
}),Available strategies:
| Strategy | Constant | Description |
|---|---|---|
| Round-robin | LoadBalancerRoundRobin | Cycles through targets sequentially |
| Weighted round-robin | LoadBalancerWeightedRoundRobin | Distributes based on target weights |
| Random | LoadBalancerRandom | Selects a target at random |
| Least connections | LoadBalancerLeastConn | Sends to the target with fewest active connections |
| Consistent hash | LoadBalancerConsistentHash | Routes based on a hash of the request (sticky sessions) |
Step 4 -- Enable circuit breakers
Add circuit breaker protection to prevent cascading failures:
bastion.NewExtension(
bastion.WithCircuitBreaker(bastion.CircuitBreakerConfig{
Enabled: true,
FailureThreshold: 5,
ResetTimeout: 30 * time.Second,
}),
bastion.WithRoute(bastion.RouteConfig{
Path: "/users/*",
Targets: []bastion.TargetConfig{
{URL: "http://user-service:8080", Weight: 1},
},
StripPrefix: true,
Protocol: bastion.ProtocolHTTP,
Enabled: true,
}),
)When a target accumulates 5 consecutive failures, the circuit opens and requests are rejected immediately. After 30 seconds the circuit enters half-open state and allows a single probe request. If it succeeds, the circuit closes. If it fails, the circuit re-opens.
Step 5 -- Add rate limiting
Protect upstreams from traffic spikes with token-bucket rate limiting:
bastion.NewExtension(
bastion.WithRateLimiting(bastion.RateLimitConfig{
Enabled: true,
RequestsPerSec: 1000,
Burst: 100,
}),
// ... routes
)This applies a global rate limit. For per-route limits, set RateLimit on individual RouteConfig entries. Per-client rate limiting uses the client IP address as the bucket key.
Step 6 -- Enable health checks
Active health probes verify upstream availability before routing traffic:
bastion.NewExtension(
bastion.WithHealthCheck(bastion.HealthCheckConfig{
Enabled: true,
Interval: 10 * time.Second,
Path: "/health",
}),
// ... routes
)Bastion sends HTTP GET requests to the configured path on each target at the specified interval. Targets that fail health checks are marked unhealthy and excluded from the load balancer pool. They are automatically re-added when health checks pass again.
Step 7 -- YAML configuration
Instead of programmatic configuration, you can define the entire gateway in YAML:
bastion:
enabled: true
basePath: "/api"
routes:
- path: "/users/*"
targets:
- url: "http://user-service:8080"
weight: 1
stripPrefix: true
protocol: http
enabled: true
circuitBreaker:
enabled: true
failureThreshold: 5
resetTimeout: 30s
rateLimiting:
enabled: true
requestsPerSec: 1000
burst: 100
healthCheck:
enabled: true
interval: 10s
path: "/health"When using YAML, the Forge application loads configuration automatically. See the Configuration page for the full key reference.
Step 8 -- Integrate with FARP discovery (optional)
If your upstream services register themselves via FARP (Forge's service discovery), Bastion can discover them automatically:
bastion.NewExtension(
bastion.WithDiscovery(bastion.DiscoveryConfig{
Enabled: true,
WatchMode: true,
AutoPrefix: true,
}),
// No manual routes needed -- discovered services are registered automatically
)Or via YAML:
bastion:
discovery:
enabled: true
watchMode: true
autoPrefix: trueWith watchMode, Bastion monitors FARP for service registrations and deregistrations in real time. With autoPrefix, each discovered service gets a route prefix matching its service name (e.g., a service named users gets the route /users/*).
Discovered routes are merged with any manually configured routes. Manual routes take precedence when paths overlap.
Step 9 -- Enable the admin dashboard (optional)
Bastion ships with a ForgeUI-based admin dashboard and REST API:
bastion.NewExtension(
bastion.WithDashboard(bastion.DashboardConfig{
Enabled: true,
BasePath: "/bastion",
Realtime: true,
}),
// ... routes
)Or via YAML:
bastion:
dashboard:
enabled: true
basePath: "/bastion"
realtime: trueThis exposes the dashboard at /bastion, the admin REST API at /bastion/api/*, an aggregated OpenAPI spec at /bastion/openapi.json, a Swagger UI at /bastion/swagger, and a real-time WebSocket event stream at /bastion/ws.
Next steps
- Architecture -- Understand the request pipeline, middleware chain, and extension lifecycle.
- Routes -- Path matching, protocol selection, and per-route overrides.
- Upstreams -- Target configuration, health states, and connection pooling.
- Protocols -- HTTP, WebSocket, SSE, and gRPC proxying.
- Configuration -- Full configuration reference with YAML keys and hot-reload behavior.