A lightweight, high-performance API Gateway built with Go, featuring rate limiting, API key management, and reverse proxy capabilities.
- Rate Limiting: Token bucket algorithm with Redis backend
- API Key Management: Create, list, revoke, and delete API keys
- Reverse Proxy: Route requests to multiple backend services
- Circuit Breaker: Prevent cascading failures with automatic recovery
- Retry Logic: Automatic retry with exponential backoff for failed requests
- Admin Authentication: Basic auth protection for admin endpoints
- Prometheus Metrics: Request count, latency percentiles, error rates
- Middleware Chain: Logging, CORS, authentication, rate limiting
- Graceful Shutdown: Clean shutdown with connection draining
- Health Checks: Monitor gateway and backend service health
- Language: Go 1.22+
- Cache/Storage: Redis 7+
- Container: Docker & Docker Compose
| Method | Endpoint | Description |
|---|---|---|
| GET | /health |
Health check |
| GET | /info |
Gateway info and registered services |
| GET | /metrics |
Prometheus metrics endpoint |
| GET | /services/health |
Backend services health status |
| POST | /admin/apikeys |
Create new API key |
| GET | /admin/apikeys |
List all API keys |
| POST | /admin/apikeys/{id}/revoke |
Revoke an API key |
| DELETE | /admin/apikeys/{id} |
Delete an API key |
| GET | /admin/circuit-breakers |
List all circuit breaker stats |
| POST | /admin/circuit-breakers/{name}/reset |
Reset specific circuit breaker |
| POST | /admin/circuit-breakers/reset |
Reset all circuit breakers |
All other requests are proxied to backend services based on path prefix:
| Path Prefix | Target Service |
|---|---|
/api/auth/* |
Auth Service |
/api/users/* |
User Service |
- Go 1.22+
- Docker & Docker Compose
- Redis 7+
- Clone the repository:
git clone https://github.com/bimakw/api-gateway.git
cd api-gateway- Copy environment file:
cp .env.example .env- Install dependencies:
go mod download- Run with Docker:
make docker-upOr run locally:
# Start Redis first
docker run -d -p 6379:6379 redis:7-alpine
# Run the gateway
make run| Variable | Description | Default |
|---|---|---|
HOST |
Server host | 0.0.0.0 |
PORT |
Server port | 8081 |
REDIS_HOST |
Redis host | localhost |
REDIS_PORT |
Redis port | 6379 |
REDIS_PASSWORD |
Redis password | `` |
REDIS_DB |
Redis database | 0 |
RATE_LIMIT_RPM |
Requests per minute | 60 |
RATE_LIMIT_BURST |
Burst size | 10 |
CB_MAX_FAILURES |
Max failures before circuit opens | 5 |
CB_RESET_TIMEOUT_SECONDS |
Seconds before circuit tries half-open | 30 |
CB_HALF_OPEN_MAX_REQUESTS |
Max requests in half-open state | 3 |
CB_SUCCESS_THRESHOLD |
Successes needed to close circuit | 2 |
RETRY_MAX_RETRIES |
Maximum retry attempts | 3 |
RETRY_INITIAL_DELAY_MS |
Initial delay before first retry (ms) | 100 |
RETRY_MAX_DELAY_MS |
Maximum delay between retries (ms) | 5000 |
RETRY_MULTIPLIER |
Delay multiplier for exponential backoff | 2.0 |
RETRY_JITTER_FACTOR |
Randomness factor to prevent thundering herd | 0.1 |
ADMIN_AUTH_ENABLED |
Enable/disable admin authentication | true |
ADMIN_USERNAME |
Admin username for Basic Auth | admin |
ADMIN_PASSWORD |
Admin password (required if enabled) | `` |
AUTH_SERVICE_URL |
Auth service URL | http://localhost:8080 |
USER_SERVICE_URL |
User service URL | http://localhost:8082 |
curl -X POST http://localhost:8081/admin/apikeys \
-H "Content-Type: application/json" \
-d '{"name": "my-app", "rate_limit": 100}'Response:
{
"status": "success",
"message": "API key created. Save the raw_key - it won't be shown again!",
"data": {
"api_key": {
"id": "abc123",
"name": "my-app",
"rate_limit": 100,
"active": true
},
"raw_key": "your-secret-api-key-here"
}
}# Via X-API-Key header
curl -X GET http://localhost:8081/api/auth/me \
-H "X-API-Key: your-secret-api-key-here"
# Or via Authorization header
curl -X GET http://localhost:8081/api/auth/me \
-H "Authorization: Bearer your-secret-api-key-here"Response headers include:
X-RateLimit-Remaining: Remaining requests in current windowX-RateLimit-Reset: Unix timestamp when the window resets
curl http://localhost:8081/healthThe gateway implements a token bucket algorithm:
- Each client (identified by IP or API key) has a bucket of tokens
- Tokens refill at a rate of
RATE_LIMIT_RPMper minute - Maximum bucket size is
RATE_LIMIT_BURST - Each request consumes one token
- When bucket is empty, requests receive
429 Too Many Requests
API keys can have custom rate limits that override the default.
The gateway implements the circuit breaker pattern to prevent cascading failures:
┌─────────────────────────────────────────────────────────┐
│ Circuit Breaker │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ CLOSED │─────►│ OPEN │─────►│HALF-OPEN │ │
│ │ │ │ │ │ │ │
│ └────▲─────┘ └──────────┘ └────┬─────┘ │
│ │ │ │
│ └────────────────────────────────────┘ │
│ (success threshold reached) │
└─────────────────────────────────────────────────────────┘
States:
- Closed: Normal operation, requests pass through
- Open: Circuit is tripped, requests fail fast with 503
- Half-Open: Testing if service recovered, limited requests allowed
Transitions:
- Closed → Open: After
CB_MAX_FAILURESconsecutive failures (5xx responses) - Open → Half-Open: After
CB_RESET_TIMEOUT_SECONDShave passed - Half-Open → Closed: After
CB_SUCCESS_THRESHOLDconsecutive successes - Half-Open → Open: On any failure
# Get all circuit breaker stats
curl http://localhost:8081/admin/circuit-breakersResponse:
{
"status": "success",
"data": [
{
"name": "auth-service",
"state": "closed",
"failures": 0,
"consecutive_successes": 5
},
{
"name": "user-service",
"state": "open",
"failures": 5,
"consecutive_successes": 0,
"last_failure": "2024-01-01T12:00:00Z"
}
]
}# Reset specific circuit breaker
curl -X POST http://localhost:8081/admin/circuit-breakers/user-service/reset
# Reset all circuit breakers
curl -X POST http://localhost:8081/admin/circuit-breakers/resetAdmin endpoints (/admin/*) are protected with HTTP Basic Authentication.
| Endpoint | Description |
|---|---|
POST /admin/apikeys |
Create API key |
GET /admin/apikeys |
List API keys |
POST /admin/apikeys/{id}/revoke |
Revoke API key |
DELETE /admin/apikeys/{id} |
Delete API key |
GET /admin/circuit-breakers |
List circuit breakers |
POST /admin/circuit-breakers/{name}/reset |
Reset circuit breaker |
POST /admin/circuit-breakers/reset |
Reset all circuit breakers |
# Enable admin auth (default: true)
export ADMIN_AUTH_ENABLED=true
# Set admin credentials
export ADMIN_USERNAME=admin
export ADMIN_PASSWORD=your-secure-passwordIMPORTANT: If ADMIN_AUTH_ENABLED=true and ADMIN_PASSWORD is not set, the gateway will fail to start.
# Access admin endpoint with Basic Auth
curl -u admin:your-password http://localhost:8081/admin/apikeys
# Or using Authorization header
curl -H "Authorization: Basic $(echo -n 'admin:your-password' | base64)" \
http://localhost:8081/admin/apikeys- Constant-time comparison: Prevents timing attacks
- SHA256 hashing: Credentials are hashed before comparison
- Failed attempt logging: Unauthorized attempts are logged with client IP
To disable admin authentication (not recommended for production):
export ADMIN_AUTH_ENABLED=falseThe gateway automatically retries failed requests to backend services using exponential backoff.
Request → Backend Service
↓
Failed? (502, 503, 504)
↓
Retry with delay
↓
delay = initial_delay * (multiplier ^ attempt) + jitter
↓
Max retries exceeded?
↓
Return error response
The following status codes trigger automatic retry:
502 Bad Gateway503 Service Unavailable504 Gateway Timeout
Delays increase exponentially with each retry:
- Attempt 1: 100ms
- Attempt 2: 200ms
- Attempt 3: 400ms
- (capped at max_delay)
Jitter is added to prevent thundering herd when multiple requests fail simultaneously.
When a request is retried, the response includes:
X-Retry-Count: Number of retries performed
# Set retry configuration
export RETRY_MAX_RETRIES=3
export RETRY_INITIAL_DELAY_MS=100
export RETRY_MAX_DELAY_MS=5000
export RETRY_MULTIPLIER=2.0
export RETRY_JITTER_FACTOR=0.1To disable retries, set RETRY_MAX_RETRIES=0.
The gateway exposes Prometheus-compatible metrics at /metrics endpoint.
| Metric | Type | Description |
|---|---|---|
gateway_uptime_seconds |
gauge | Time since gateway started |
gateway_requests_in_flight |
gauge | Current number of requests being processed |
gateway_rate_limited_total |
counter | Total number of rate limited requests |
gateway_http_requests_total |
counter | Total HTTP requests by method, path, status |
gateway_http_request_duration_ms |
summary | Request duration percentiles (p50, p95, p99) |
gateway_backend_requests_total |
counter | Total requests to backend services |
gateway_backend_errors_total |
counter | Total errors from backend services |
gateway_circuit_breaker_state |
gauge | Circuit breaker state (1=closed, 0.5=half-open, 0=open) |
gateway_circuit_breaker_trips_total |
counter | Total circuit breaker trips |
# Get metrics in Prometheus format
curl http://localhost:8081/metrics
# Get metrics in JSON format
curl -H "Accept: application/json" http://localhost:8081/metricsAdd to your prometheus.yml:
scrape_configs:
- job_name: 'api-gateway'
static_configs:
- targets: ['localhost:8081']
metrics_path: '/metrics'
scrape_interval: 15sKey queries for Grafana dashboards:
# Request rate (per second)
rate(gateway_http_requests_total[5m])
# Error rate (5xx responses)
rate(gateway_http_requests_total{status=~"5.."}[5m])
# P95 latency
gateway_http_request_duration_ms{quantile="0.95"}
# Rate limited requests
rate(gateway_rate_limited_total[5m])
# Circuit breaker state
gateway_circuit_breaker_state
api-gateway/
├── cmd/
│ └── gateway/
│ └── main.go # Entry point
├── config/
│ └── config.go # Configuration
├── internal/
│ ├── apikey/
│ │ └── apikey.go # API key management
│ ├── circuitbreaker/
│ │ ├── circuitbreaker.go # Circuit breaker implementation
│ │ └── registry.go # Circuit breaker registry
│ ├── handler/
│ │ └── handler.go # HTTP handlers
│ ├── health/
│ │ └── health.go # Health checker
│ ├── metrics/
│ │ └── metrics.go # Prometheus metrics
│ ├── middleware/
│ │ └── middleware.go # Middleware chain
│ ├── proxy/
│ │ └── proxy.go # Reverse proxy
│ ├── ratelimit/
│ │ └── ratelimit.go # Rate limiter
│ └── retry/
│ └── retry.go # Retry with exponential backoff
├── .env.example
├── .gitignore
├── docker-compose.yml
├── Dockerfile
├── go.mod
├── go.sum
├── Makefile
└── README.md
# Run in development mode
make run
# Run tests
make test
# Format code
make fmt
# Build binary
make buildMIT License