Routes
Route configuration, path matching, protocol selection, and per-route overrides.
A route defines how incoming requests are matched and forwarded to upstream targets. Each route has a path pattern, one or more targets, a protocol, and optional overrides for load balancing, rate limiting, authentication, and caching.
RouteConfig
The RouteConfig struct is the primary way to define routes programmatically:
type RouteConfig struct {
// Path is the URL pattern to match (e.g., "/users/*", "/api/v1/orders/*").
Path string
// Targets is the list of upstream targets for this route.
Targets []TargetConfig
// StripPrefix removes the matched path prefix before forwarding.
StripPrefix bool
// Protocol specifies the proxy protocol (HTTP, WebSocket, SSE, gRPC).
Protocol Protocol
// Enabled controls whether this route is active.
Enabled bool
// LoadBalancer overrides the global load balancing strategy for this route.
LoadBalancer LoadBalancerStrategy
// RateLimit overrides the global rate limit for this route.
RateLimit *RateLimitConfig
// Auth sets route-specific authentication requirements.
Auth *AuthConfig
// Cache sets route-specific response caching policy.
Cache *CacheConfig
// Timeout overrides the global upstream timeout for this route.
Timeout time.Duration
// RetryPolicy overrides the global retry configuration for this route.
RetryPolicy *RetryConfig
// Metadata is a free-form map for custom route annotations.
Metadata map[string]string
}Registering routes
Routes are registered via the WithRoute option function:
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,
}),
)Path matching
Bastion uses path patterns to match incoming requests to routes. Patterns support these forms:
| Pattern | Matches | Example |
|---|---|---|
/users/* | Any path starting with /users/ | /users/42, /users/42/profile |
/api/v1/orders/* | Any path starting with /api/v1/orders/ | /api/v1/orders/123 |
/health | Exact match only | /health |
The * wildcard matches one or more path segments. Routes are matched in registration order -- the first matching route wins.
Path conflicts
When multiple routes could match the same request, the most specific route wins. Exact matches take precedence over wildcard matches. If two wildcard routes overlap, the one registered first wins.
Protocol selection
Each route specifies a protocol that determines how requests are proxied:
const (
ProtocolHTTP Protocol = "http"
ProtocolWebSocket Protocol = "websocket"
ProtocolSSE Protocol = "sse"
ProtocolGRPC Protocol = "grpc"
)| Protocol | Use case |
|---|---|
ProtocolHTTP | Standard HTTP/HTTPS API traffic. Also handles WebSocket upgrades automatically. |
ProtocolWebSocket | Dedicated WebSocket routes where all traffic is WebSocket. |
ProtocolSSE | Server-Sent Events streaming endpoints. Disables response buffering. |
ProtocolGRPC | gRPC services. Forwards HTTP/2 frames including trailers. |
For most services, ProtocolHTTP is the right choice. It handles both regular HTTP requests and WebSocket upgrade requests transparently.
Strip prefix
When StripPrefix is true, Bastion removes the matched path prefix before forwarding the request to the upstream.
// Route: /users/* with StripPrefix: true
// Request: GET /users/42/profile
// Upstream: GET /42/profile
// Route: /users/* with StripPrefix: false
// Request: GET /users/42/profile
// Upstream: GET /users/42/profileThis is useful when the upstream service does not expect the gateway's path prefix. Most microservice deployments use StripPrefix: true.
Enable and disable
Routes have an Enabled field that controls whether they accept traffic:
bastion.RouteConfig{
Path: "/users/*",
Enabled: true, // route is active
}Disabled routes return 404 Not Found as if they do not exist. You can enable and disable routes at runtime through the admin API:
POST /bastion/api/routes/:id/enable
POST /bastion/api/routes/:id/disableStatic vs discovered routes
Bastion supports two sources of routes:
Static routes are defined in code (via WithRoute) or in YAML configuration. They are loaded at startup and persist across restarts.
Discovered routes are created automatically by the FARP service discovery integration. When a new service registers itself via FARP, Bastion creates a route for it. When the service deregisters, the route is removed.
Static routes take precedence over discovered routes when paths overlap. Discovered routes are tagged with metadata indicating their source.
Per-route overrides
Several global settings can be overridden at the route level:
Rate limiting
bastion.RouteConfig{
Path: "/uploads/*",
RateLimit: &bastion.RateLimitConfig{
Enabled: true,
RequestsPerSec: 10,
Burst: 5,
},
}Load balancing
bastion.RouteConfig{
Path: "/users/*",
LoadBalancer: bastion.LoadBalancerLeastConn,
}Authentication
bastion.RouteConfig{
Path: "/admin/*",
Auth: &bastion.AuthConfig{
Type: bastion.AuthBearer,
Secret: "my-admin-token",
},
}Response caching
bastion.RouteConfig{
Path: "/products/*",
Cache: &bastion.CacheConfig{
Enabled: true,
TTL: 5 * time.Minute,
},
}Timeout
bastion.RouteConfig{
Path: "/reports/*",
Timeout: 60 * time.Second,
}Retry policy
bastion.RouteConfig{
Path: "/payments/*",
RetryPolicy: &bastion.RetryConfig{
MaxRetries: 3,
Strategy: bastion.RetryExponential,
BaseDelay: 100 * time.Millisecond,
MaxDelay: 5 * time.Second,
Jitter: true,
},
}YAML route configuration
Routes can also be defined in YAML:
bastion:
routes:
- path: "/users/*"
targets:
- url: "http://user-service:8080"
weight: 1
stripPrefix: true
protocol: http
enabled: true
loadBalancer: round-robin
timeout: 30s
rateLimit:
enabled: true
requestsPerSec: 100
burst: 20
cache:
enabled: true
ttl: 5m