// Five Example: HTTP REST API Server // // PRG handles business logic (customer data, search) // Go handles HTTP serving, JSON, concurrency // // Usage: five run go_httpserver.prg // curl http://localhost:8080/api/customers // curl http://localhost:8080/api/customers/search?name=John PROCEDURE Main() LOCAL cPort cPort := "8080" ? "=== Five REST API Server ===" ? "Powered by Harbour data + Go net/http" ? ? "Starting server on port " + cPort + "..." ? "Endpoints:" ? " GET /api/customers - list all customers" ? " GET /api/customers/search - search by name (?name=xxx)" ? " POST /api/customers - add customer (JSON body)" ? " GET /api/stats - server statistics" ? " GET /health - health check" ? ? "Press Ctrl+C to stop" GoHttpServe(cPort) RETURN FUNCTION GetCustomers() LOCAL aResult aResult := {} AAdd(aResult, { "id" => 1, "name" => "Charles Kwon", "city" => "Seoul", "balance" => 15000.50 }) AAdd(aResult, { "id" => 2, "name" => "John Smith", "city" => "New York", "balance" => 8200.00 }) AAdd(aResult, { "id" => 3, "name" => "Maria Garcia", "city" => "Madrid", "balance" => 12300.75 }) AAdd(aResult, { "id" => 4, "name" => "Yuki Tanaka", "city" => "Tokyo", "balance" => 9800.25 }) AAdd(aResult, { "id" => 5, "name" => "Hans Mueller", "city" => "Berlin", "balance" => 6500.00 }) RETURN aResult FUNCTION SearchCustomers(cSearch) LOCAL aAll, aResult, i aAll := GetCustomers() aResult := {} FOR i := 1 TO Len(aAll) IF Upper(cSearch) $ Upper(aAll[i]["name"]) AAdd(aResult, aAll[i]) ENDIF NEXT RETURN aResult #pragma BEGINDUMP import ( "encoding/json" "five/hbrt" "fmt" "net/http" "strings" "sync/atomic" "time" ) var requestCount int64 var startTime time.Time func init() { hbrt.HB_FUNC("GOHTTPSERVE", goHttpServe) } func goHttpServe(ctx *hbrt.HBContext) { port := ctx.ParC(1) if port == "" { port = "8080" } startTime = time.Now() mux := http.NewServeMux() mux.HandleFunc("/health", handleHealth) mux.HandleFunc("/api/stats", handleStats) mux.HandleFunc("/api/customers", handleCustomers) mux.HandleFunc("/api/customers/search", handleSearch) server := &http.Server{ Addr: ":" + port, Handler: withLogging(mux), ReadTimeout: 15 * time.Second, WriteTimeout: 15 * time.Second, } if err := server.ListenAndServe(); err != nil { ctx.RetC("Error: " + err.Error()) } } func withLogging(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { atomic.AddInt64(&requestCount, 1) start := time.Now() next.ServeHTTP(w, r) fmt.Printf(" %s %s %s [%v]\n", r.Method, r.URL.Path, r.RemoteAddr, time.Since(start)) }) } func handleHealth(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{ "status": "healthy", "uptime": time.Since(startTime).String(), }) } func handleStats(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]interface{}{ "requests": atomic.LoadInt64(&requestCount), "uptime_ms": time.Since(startTime).Milliseconds(), "engine": "Five (Harbour + Go)", }) } func handleCustomers(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") customers := []map[string]interface{}{ {"id": 1, "name": "Charles Kwon", "city": "Seoul", "balance": 15000.50}, {"id": 2, "name": "John Smith", "city": "New York", "balance": 8200.00}, {"id": 3, "name": "Maria Garcia", "city": "Madrid", "balance": 12300.75}, {"id": 4, "name": "Yuki Tanaka", "city": "Tokyo", "balance": 9800.25}, {"id": 5, "name": "Hans Mueller", "city": "Berlin", "balance": 6500.00}, } if r.Method == "POST" { var newCustomer map[string]interface{} if err := json.NewDecoder(r.Body).Decode(&newCustomer); err != nil { http.Error(w, `{"error": "invalid JSON"}`, http.StatusBadRequest) return } newCustomer["id"] = len(customers) + 1 customers = append(customers, newCustomer) w.WriteHeader(http.StatusCreated) json.NewEncoder(w).Encode(newCustomer) return } json.NewEncoder(w).Encode(customers) } func handleSearch(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") query := strings.ToLower(r.URL.Query().Get("name")) if query == "" { http.Error(w, `{"error": "name parameter required"}`, http.StatusBadRequest) return } customers := []map[string]interface{}{ {"id": 1, "name": "Charles Kwon", "city": "Seoul", "balance": 15000.50}, {"id": 2, "name": "John Smith", "city": "New York", "balance": 8200.00}, {"id": 3, "name": "Maria Garcia", "city": "Madrid", "balance": 12300.75}, {"id": 4, "name": "Yuki Tanaka", "city": "Tokyo", "balance": 9800.25}, {"id": 5, "name": "Hans Mueller", "city": "Berlin", "balance": 6500.00}, } var results []map[string]interface{} for _, c := range customers { if strings.Contains(strings.ToLower(c["name"].(string)), query) { results = append(results, c) } } json.NewEncoder(w).Encode(results) } #pragma ENDDUMP