HTTP REST API
Complete reference for the Easy AppServer HTTP REST API.
Overview
The AppServer HTTP API provides REST endpoints for health checks, proxy routing, and future resource management. Built on Go 1.22+ enhanced ServeMux with full HTTP/2 support.
Base URL:
- Local:
http://localhost:8080 - Production: Configured via
APPSERVER_HTTP_PORT
Architecture
The HTTP server includes:
- HTTP/2 Support: Full HTTP/2 with TLS, h2c (HTTP/2 Cleartext) without TLS
- Middleware Stack: 9-layer middleware for observability, security, and error handling
- Dynamic Proxy: Route requests to app backends based on manifest configuration
- Health Endpoints: Built-in health and readiness checks
- Graceful Shutdown: Clean connection handling on SIGTERM/SIGINT
Middleware Stack
All requests pass through these middleware layers in order:
- Request ID (
middleware/request_id.go) - Generate unique X-Request-ID for tracing - Logging (
middleware/logging.go) - Structured request/response logging - Metrics (
middleware/metrics.go) - Prometheus metrics collection - Tracing (
middleware/tracing.go) - OpenTelemetry distributed tracing - CORS (
middleware/cors.go) - Cross-Origin Resource Sharing headers - Authentication (
middleware/auth.go) - User (Kratos) or app (signature) authentication - Authorization (
middleware/authz.go) - OpenFGA permission checks - Error Handling (
middleware/errors.go) - Standard error responses - Recovery (
middleware/recovery.go) - Panic recovery with error logging
Core Endpoints
Health Check
Returns server health status and uptime.
GET /health
Response: 200 OK
{
"status": "healthy",
"uptime": 3600.5
}
Implementation: pkg/v2/presentation/http/handlers/health.go:27
Use Cases:
- Monitoring system health checks
- Load balancer health probes
- Container orchestration health checks
Readiness Check
Returns readiness status for load balancers.
GET /ready
Response: 200 OK
{
"status": "ready"
}
Implementation: pkg/v2/presentation/http/handlers/health.go:41
Future Enhancement: Will check database, RabbitMQ, OpenFGA connectivity before returning ready.
Use Cases:
- Kubernetes readiness probes
- Load balancer backend checks
- Rolling deployment coordination
Dynamic Proxy Routing
The AppServer dynamically routes HTTP requests to app backends based on manifest configuration.
How It Works
Client Request
│
▼
┌─────────────────────────────────────┐
│ AppServer HTTP (Port 8080) │
│ │
│ 1. Match route from app manifest │
│ 2. Check permissions (OpenFGA) │
│ 3. Validate rate limits │
│ 4. Forward to upstream │
│ 5. Return response │
└─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ App Backend (e.g., todos-bff) │
│ http://todos-bff:8080 │
└─────────────────────────────────────┘
Route Configuration
Routes are defined in the app manifest:
message WebApi {
string base_path = 1; // e.g., "/api/apps/todos"
string upstream_base_url = 2; // e.g., "http://todos-bff:8080"
bool strip_base_path = 3; // Default: true
int32 timeout_ms = 4; // Request timeout (default: 30000)
repeated string forward_headers = 5; // Headers to forward
repeated RouteSpec routes = 6; // Specific route overrides
repeated string required_permissions = 7; // Permissions required
RateLimit default_rate_limit = 8; // Rate limiting config
HealthCheck health_check = 9; // Upstream health check
string openapi_url = 10; // OpenAPI spec URL
}
message RouteSpec {
string path = 1; // e.g., "/items/{id}"
repeated string methods = 2; // e.g., ["GET", "POST"]
repeated string required_permissions = 3; // Override permissions
RateLimit rate_limit = 4; // Override rate limit
int32 timeout_ms = 5; // Override timeout
}
Example Request Flow
Manifest Configuration:
web_api:
base_path: "/api/apps/todos"
upstream_base_url: "http://todos-bff:8080"
strip_base_path: true
forward_headers:
- "Authorization"
- "Content-Type"
- "X-Request-ID"
required_permissions:
- "todos:read"
Client Request:
GET /api/apps/todos/items/123 HTTP/1.1
Host: localhost:8080
Authorization: Bearer <kratos-session-token>
Content-Type: application/json
Proxied Request (with strip_base_path: true):
GET /items/123 HTTP/1.1
Host: todos-bff:8080
Authorization: Bearer <kratos-session-token>
Content-Type: application/json
X-Request-ID: <generated-uuid>
X-User-ID: <kratos-user-id>
Without stripping (strip_base_path: false):
GET /api/apps/todos/items/123 HTTP/1.1
Host: todos-bff:8080
Authorization: Bearer <kratos-session-token>
Content-Type: application/json
X-Request-ID: <generated-uuid>
X-User-ID: <kratos-user-id>
Path-Based Routing
The proxy matches routes using Go 1.22+ enhanced patterns:
// Exact match
"/api/apps/todos/items"
// Wildcard match
"/api/apps/todos/{path...}"
// Parameter match
"/api/apps/todos/items/{id}"
Header Forwarding
Only headers in the forward_headers allowlist are forwarded to upstreams:
Common Headers:
Authorization- Bearer tokens, API keysContent-Type- Request/response content typeAccept- Accepted response typesX-Request-ID- Request correlation IDX-User-ID- User identifier (added by auth middleware)X-App-Name- App identifier (added by auth middleware)
Security: Sensitive headers like Cookie, X-App-Signature are NOT forwarded unless explicitly listed.
Authentication
The HTTP server supports two authentication methods:
User Authentication (Kratos)
Users authenticate via Ory Kratos sessions:
curl -H "Cookie: ory_kratos_session=<session-token>" \
http://localhost:8080/api/apps/todos/items
Flow:
- User logs in via Kratos
- Kratos session cookie set
- AppServer validates session with Kratos public API
- User ID extracted and added to request context
App Authentication (Signature)
Apps authenticate using RSA signature verification:
curl -H "X-App-Name: myapp" \
-H "X-App-Signature: <base64-rsa-signature>" \
-H "X-Request-Timestamp: 2024-11-18T12:00:00Z" \
http://localhost:8080/api/apps/todos/items
Signature Generation:
- Concatenate:
method + url + timestamp + body - Sign with app's private RSA key
- Base64 encode signature
- Include in
X-App-Signatureheader
Verification:
- Signature verified against app's public key (from certificate)
- Timestamp checked for replay protection (5-minute window)
- Clock skew tolerance: 30 seconds
Implementation: pkg/v2/infrastructure/auth/signature.go
Authorization
Authorization is enforced via OpenFGA with multi-level permission caching.
Permission Checks
Before proxying requests, the server checks required permissions:
web_api:
base_path: "/api/apps/todos"
required_permissions:
- "todos:read"
routes:
- path: "/items"
methods: ["POST"]
required_permissions:
- "todos:write"
Check Flow:
- Extract user/app from auth context
- Check local permission cache (in-memory)
- On cache miss, check Redis cache
- On Redis miss, check OpenFGA
- Cache result with TTL (1s local, 5min Redis)
Implementation: pkg/v2/infrastructure/permission/cache/
Permission Format
Permissions follow the pattern: <resource>:<action>
Examples:
app:install- Install appsapp:uninstall- Uninstall appssettings:read- Read settingssettings:write- Update settingstodos:read- Read todostodos:write- Create/update todos
Rate Limiting
Rate limiting is enforced per-route using Redis-backed sliding window counters.
Configuration
message RateLimit {
int32 requests = 1; // Max requests
int32 window_seconds = 2; // Time window in seconds
string key_pattern = 3; // Rate limit key pattern
}
Example:
web_api:
default_rate_limit:
requests: 100
window_seconds: 60 # 100 requests per minute
key_pattern: "user:{user_id}"
Rate Limit Keys
Rate limits can be scoped by:
- User:
user:{user_id} - App:
app:{app_name} - IP:
ip:{remote_addr} - Global:
global
Response Headers
Rate limit status is returned in response headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1700000000
Rate Limit Exceeded
Response: 429 Too Many Requests
{
"error": "rate limit exceeded",
"code": "RATE_LIMIT_EXCEEDED",
"details": {
"limit": 100,
"window": 60,
"retry_after": 15
}
}
Implementation: pkg/v2/infrastructure/ratelimit/
HTTP/2 Support
The server supports HTTP/2 with and without TLS:
With TLS (Full HTTP/2)
server:
http_port: 8080
tls_enabled: true
tls_cert_file: "/path/to/cert.pem"
tls_key_file: "/path/to/key.pem"
http2_enabled: true
Without TLS (h2c)
server:
http_port: 8080
tls_enabled: false
http2_enabled: true # Enables h2c (HTTP/2 Cleartext)
Benefits:
- Multiplexing: Multiple requests over single connection
- Header compression: Reduced bandwidth
- Server push: Proactive resource delivery (future)
- Binary protocol: Faster parsing
Server Configuration
Complete configuration options:
server:
# Ports
http_port: 8080
address: "0.0.0.0"
# Timeouts
read_timeout: 30s # Read request timeout
write_timeout: 30s # Write response timeout
idle_timeout: 120s # Keep-alive idle timeout
shutdown_timeout: 30s # Graceful shutdown timeout
# TLS
tls_enabled: false
tls_cert_file: ""
tls_key_file: ""
# HTTP/2
http2_enabled: true
# Limits
max_header_bytes: 1048576 # 1MB
Error Responses
All errors follow a standard format:
{
"error": "human-readable error message",
"code": "ERROR_CODE",
"details": {
"field": "additional context"
}
}
Status Codes
| Code | Status | Description |
|---|---|---|
| 200 | OK | Success |
| 400 | Bad Request | Invalid request syntax or validation error |
| 401 | Unauthorized | Authentication required or failed |
| 403 | Forbidden | Insufficient permissions |
| 404 | Not Found | Resource or route not found |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Internal Server Error | Server-side error |
| 502 | Bad Gateway | Upstream service error |
| 503 | Service Unavailable | Service temporarily down |
| 504 | Gateway Timeout | Upstream timeout |
Error Codes
| Code | Description |
|---|---|
INVALID_REQUEST | Request validation failed |
AUTHENTICATION_REQUIRED | No authentication provided |
AUTHENTICATION_FAILED | Invalid credentials or signature |
PERMISSION_DENIED | Insufficient permissions |
RATE_LIMIT_EXCEEDED | Too many requests |
NOT_FOUND | Resource not found |
UPSTREAM_ERROR | Backend service error |
UPSTREAM_TIMEOUT | Backend service timeout |
INTERNAL_ERROR | Server internal error |
Testing
Health Checks
# Health check
curl http://localhost:8080/health
# Expected response
{"status":"healthy","uptime":3600.5}
# Readiness check
curl http://localhost:8080/ready
# Expected response
{"status":"ready"}
Authenticated Requests
User Authentication:
curl -H "Cookie: ory_kratos_session=<token>" \
http://localhost:8080/api/apps/todos/items
App Authentication:
# Generate signature (using openssl)
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
MESSAGE="GET/api/apps/todos/items${TIMESTAMP}"
SIGNATURE=$(echo -n "$MESSAGE" | openssl dgst -sha256 -sign app-private-key.pem | base64)
curl -H "X-App-Name: myapp" \
-H "X-App-Signature: $SIGNATURE" \
-H "X-Request-Timestamp: $TIMESTAMP" \
http://localhost:8080/api/apps/todos/items
Rate Limit Testing
# Make multiple requests
for i in {1..110}; do
curl -i http://localhost:8080/api/apps/todos/items
sleep 0.1
done
# After 100 requests, expect:
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1700000060
Observability
The HTTP server integrates with the telemetry stack:
Metrics (Prometheus)
http_requests_total{method, path, status}- Total requestshttp_request_duration_seconds{method, path}- Request duration histogramhttp_requests_in_flight- Current active requestshttp_response_size_bytes{method, path}- Response size histogram
Grafana: http://localhost:3000 (pre-configured dashboards)
Tracing (OpenTelemetry)
All requests are traced with:
- Span name:
HTTP {method} {path} - Attributes: method, path, status, user_id, app_name, request_id
- Parent/child relationships for upstream calls
Tempo: http://localhost:3200
Logging
Structured logs for each request:
{
"level": "info",
"time": "2024-11-18T12:00:00Z",
"msg": "HTTP request",
"request_id": "123e4567-e89b-12d3-a456-426614174000",
"method": "GET",
"path": "/api/apps/todos/items",
"status": 200,
"duration_ms": 45.2,
"user_id": "user-123",
"app_name": "todos"
}
Loki: Accessible via Grafana at http://localhost:3000
Future Endpoints
Planned REST API endpoints:
App Management
GET /api/v2/apps- List all appsGET /api/v2/apps/{id}- Get app detailsPOST /api/v2/apps/{id}/install- Install appDELETE /api/v2/apps/{id}/uninstall- Uninstall app
Settings Management
GET /api/v2/apps/{id}/settings- Get app settingsPUT /api/v2/apps/{id}/settings- Update settingsPOST /api/v2/apps/{id}/settings/validate- Validate settings
Event Queries
GET /api/v2/events- Query event storeGET /api/v2/events/{id}- Get event details
OpenAPI Documentation
GET /api/v2/openapi.json- OpenAPI specificationGET /api/v2/docs- Swagger UI
Code References
- pkg/v2/presentation/http/server.go - HTTP server implementation
- pkg/v2/presentation/http/router.go - Route registration and middleware
- pkg/v2/presentation/http/handlers/health.go:27 - Health check handler
- pkg/v2/presentation/http/proxy_handler.go - Dynamic proxy implementation
- pkg/v2/presentation/http/middleware/ - Middleware implementations
- pkg/v2/infrastructure/ratelimit/ - Rate limiting implementation
- pkg/v2/infrastructure/auth/signature.go - Signature verification
Related Topics
- gRPC API - gRPC services for app integration
- GraphQL Schema - GraphQL API reference
- Proxy and Routing - HTTP proxy feature overview
- Permission System - Authorization details