An API Gateway built with Go and Gin. The project demonstrates practical backend infrastructure patterns: config-driven reverse proxying, JWT authentication, role-based authorization, rate limiting, timeout handling, retries, circuit breaking, structured logs, Prometheus metrics, and a Docker Compose demo environment.
- Config-driven route forwarding
- Health and readiness endpoints
- Request IDs and structured logging
- JWT authentication
- Role-based authorization
- Per-route rate limiting
- Redis-backed distributed rate limiting
- Upstream timeout handling
- Upstream retry handling
- Circuit breaker protection
- Standardized JSON error responses
- Prometheus metrics
- Docker Compose demo environment
- Gateway and middleware tests
flowchart LR
client[Client] --> gateway[Go API Gateway]
subgraph gateway[Go API Gateway]
requestID[Request ID]
logging[Structured Logging]
metrics[Prometheus Metrics]
auth[JWT Authentication]
roles[Role Authorization]
rateLimit[Rate Limiting]
breaker[Circuit Breaker]
proxy[Reverse Proxy]
end
requestID --> logging --> metrics --> auth --> roles --> rateLimit --> breaker --> proxy
proxy --> users[Users Service]
proxy --> other[Other Upstream Services]
The gateway reads route definitions from YAML and registers matching Gin routes at startup. Each configured route can define its upstream URL, allowed methods, auth requirements, roles, rate limit, circuit breaker, timeout, and retry policy.
go run ./cmd/gatewayThe gateway starts on http://localhost:8080.
curl http://localhost:8080/health
curl http://localhost:8080/ready
curl http://localhost:8080/metricsdocker compose up --buildThe Compose demo starts the gateway on http://localhost:8080 and a mock users service behind it.
Generate a demo JWT and call the protected users route:
TOKEN=$(go run ./cmd/token -secret change-me-in-production -sub user-123 -roles user)
curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/api/usersStart the gateway and mock users service:
docker compose up --buildIn another terminal, check the gateway health endpoints:
curl http://localhost:8080/health
curl http://localhost:8080/readyTry the protected route without a token:
curl http://localhost:8080/api/usersGenerate a valid token:
TOKEN=$(go run ./cmd/token -secret change-me-in-production -sub user-123 -roles user)Call the protected users route:
curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/api/users
curl -H "Authorization: Bearer $TOKEN" http://localhost:8080/api/users/1Inspect Prometheus metrics:
curl http://localhost:8080/metricsStop the demo:
docker compose downStart the gateway and mock users service:
docker compose up --buildIn another PowerShell window, check the gateway health endpoints:
Invoke-RestMethod http://localhost:8080/health
Invoke-RestMethod http://localhost:8080/readyTry the protected route without a token:
curl.exe -i http://localhost:8080/api/usersGenerate a valid token:
$TOKEN = go run ./cmd/token -secret change-me-in-production -sub user-123 -roles userCall the protected users route:
Invoke-RestMethod -Headers @{ Authorization = "Bearer $TOKEN" } http://localhost:8080/api/users
Invoke-RestMethod -Headers @{ Authorization = "Bearer $TOKEN" } http://localhost:8080/api/users/1Inspect rate limit headers:
$response = Invoke-WebRequest -UseBasicParsing -Headers @{ Authorization = "Bearer $TOKEN" } http://localhost:8080/api/users
$response.StatusCode
$response.Headers["X-RateLimit-Limit"]
$response.Headers["X-RateLimit-Remaining"]
$response.Headers["X-RateLimit-Reset"]Inspect Prometheus metrics:
(Invoke-WebRequest -UseBasicParsing http://localhost:8080/metrics).Content |
Select-String "gateway_requests_total"Stop the demo:
docker compose downThe default config lives at config/gateway.yaml.
server:
port: "8080"
auth:
jwt_secret: "change-me-in-production"
redis:
enabled: false
address: localhost:6379
routes:
- id: users-service
path: /api/users
upstream: http://localhost:9001
auth_required: true
roles:
- user
- admin
rate_limit:
requests: 60
window: 1m
circuit_breaker:
failure_threshold: 3
reset_timeout: 30s
timeout: 3s
retries: 2
methods:
- GET
- POSTUse CONFIG_PATH to load a different config file:
CONFIG_PATH=config/gateway.compose.yaml go run ./cmd/gatewayRate limits are configured per route. By default, the gateway uses an in-memory limiter, which is useful for local development and single-instance deployments.
Enable Redis in config to share rate limit counters across gateway instances:
redis:
enabled: true
address: redis:6379The Docker Compose demo starts Redis and uses config/gateway.compose.yaml, which enables Redis-backed rate limiting.
Protected routes expect a JWT signed with the configured auth.jwt_secret. The token subject is forwarded to upstream services as X-User-ID, and roles are read from a roles claim.
Example payload:
{
"sub": "user-123",
"roles": ["user"],
"exp": 1893456000
}Generate a compatible demo token:
go run ./cmd/token -secret change-me-in-production -sub user-123 -roles user,adminGateway-generated errors use a consistent JSON shape:
{
"error": {
"code": "upstream_timeout",
"message": "upstream timeout",
"request_id": "9f3c2f6c8a7e4b0fb77a2b64d02c1e91"
}
}Examples include missing_bearer_token, invalid_bearer_token, insufficient_role, rate_limit_exceeded, upstream_timeout, and circuit_open.
GET /health: liveness checkGET /ready: readiness checkGET /metrics: Prometheus metrics- Configured gateway routes, such as
GET /api/users
- Route configuration is loaded at startup. Runtime route updates would require a reload mechanism or an external configuration service.
- JWT validation uses a shared HMAC secret for the demo. A production deployment would typically use JWKS, key rotation, issuer validation, and audience validation.
- Redis-backed rate limiting is implemented with simple fixed-window counters. Sliding-window or token-bucket algorithms would provide smoother traffic shaping.
- The circuit breaker is process-local. In a multi-instance deployment, circuit state is not shared between gateway replicas.
- Retry handling is intentionally limited to idempotent methods such as
GET,HEAD, andOPTIONS. - The Docker Compose services are intended for local demonstration rather than hardened production deployment.
- Add JWKS-based JWT verification with issuer and audience checks.
- Support hot-reloading route configuration without restarting the gateway.
- Add token-bucket or sliding-window rate limiting for smoother request control.
- Persist or coordinate circuit breaker state across gateway instances.
- Add OpenTelemetry tracing for distributed request visibility.
- Add admin-facing route validation and configuration preview tooling.
- Add Kubernetes manifests or Helm chart for deployment examples.
Run the test suite with local caches:
GOCACHE=.gocache GOMODCACHE=.gomodcache go test ./...Or use the Makefile shortcuts:
make test
make run
make token
make docker-up
make docker-downCopy .env.example values into your shell or local environment when you want to override defaults.
This project is licensed under the MIT License.