Hyperserve Best Practices Example
This example demonstrates the correct way to use hyperserve's built-in features without reimplementing functionality.
What This Example Shows
✅ DO: Use Built-in Features
- Graceful Shutdown - No custom signal handling needed
- Request Logging - Automatic structured logging with slog
- Rate Limiting - Built-in token bucket rate limiting
- Health Checks - Separate health server on :8081
- MCP Support - Native Model Context Protocol integration
- SSE Support - Proper Server-Sent Events with helpers
- Security Headers - Pre-configured middleware stacks
- Configuration - Explicit environment binding plus application invariants
❌ DON'T: Common Anti-Patterns to Avoid
- Custom shutdown handling - hyperserve handles SIGINT/SIGTERM
- Custom logging middleware - RequestLoggerMiddleware is applied by default
- Manual MCP implementation - Use WithMCPSupport()
- Manual SSE formatting - Use NewSSEMessage() helper
- Implicit configuration - Bind deployment variables with
WithEnvironment()
Running the Example
# Basic usage
go run main.go
# With debug logging
HS_LOG_LEVEL=DEBUG go run main.go
# With custom configuration
HS_PORT=9090 HS_RATE_LIMIT=50 go run main.go
# Test protected endpoint
curl -H "Authorization: Bearer secret-token-123" http://localhost:8080/api/data
# Test MCP endpoint
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "tools/list",
"id": 1
}'
# Watch SSE stream
curl -N http://localhost:8080/api/stream
Key Takeaways
- Hyperserve is batteries-included - Most common server needs are built-in
- Use functional options - Configure with WithX() functions
- Leverage middleware stacks - SecureAPI() and SecureWeb() for common patterns
- Trust the defaults - Sensible defaults that work for most applications
- Progressive complexity - Start simple, add features as needed
Configuration Options
The example opts into HyperServe's supported environment variables with
WithEnvironment(). A bare NewServer() ignores them.
HS_PORT - Server port (default: 8080)
HEALTH_ADDR - Health check address (default: :9080)
HS_RATE_LIMIT - Requests per second (default: 100)
HS_BURST_LIMIT - Burst capacity (default: 200)
HS_LOG_LEVEL - Log level: DEBUG, INFO, WARN, or ERROR
HS_MCP_ENABLED - Enable MCP support (default: false)
HS_MCP_FILE_TOOL_ROOT - Root directory for MCP file tools
Comparison: Wrong Way vs Right Way
Logging
// ❌ WRONG: Custom logging middleware
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
// ... custom logging implementation
})
}
// ✅ RIGHT: Use built-in logging (applied automatically)
srv, _ := server.NewServer() // RequestLoggerMiddleware included by default
Shutdown
// ❌ WRONG: Custom signal handling
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, os.Interrupt)
go func() {
<-sigChan
os.Exit(0)
}()
// ✅ RIGHT: Let hyperserve handle it
srv.Run() // Handles SIGINT/SIGTERM automatically
MCP
// ❌ WRONG: Custom MCP handler
type MCPHandler struct{}
func (h *MCPHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// ... manual JSON-RPC implementation
}
// ✅ RIGHT: Use built-in MCP
srv, _ := server.NewServer(
server.WithMCPSupport("best-practices", "1.0.0"),
)
SSE
// ❌ WRONG: Manual SSE formatting
fmt.Fprintf(w, "event: %s\n", event)
fmt.Fprintf(w, "data: %s\n\n", data)
// ✅ RIGHT: Use SSE helper
msg := server.NewSSEMessage(data)
msg.Event = event
fmt.Fprint(w, msg)