package main import ( "encoding/json" "fmt" "log/slog" "net/http" "github.com/icodealot/noaa" ) type ForecastReq struct { Latitude float64 `json:"latitude"` Longitude float64 `json:"longitude"` } type ForecastResp struct { ShortForecast string `json:"shortForecast"` Temperature string `json:"temperature"` } func main() { // Using the default HTTP server // SHORTCUT: no TLS // SHORTCUT: The default HTTP server is good enough for small projects, but would need to be better configured for production // SHORTCUT: no rate limiting http.HandleFunc("GET /forecast", forecast) // SHORTCUT: This server is only stopped when the process stops. This should have a graceful shutdown. if err := http.ListenAndServe(":8080", nil); err != nil { slog.Error("stopping server", err) } } func forecast(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() // SHORTCUT: not checking the body length to see if it is unreasonably large // Decode request var foreReq ForecastReq if err := json.NewDecoder(r.Body).Decode(&foreReq); err != nil { slog.Error("decode forecast request error", err) http.Error(w, err.Error(), http.StatusBadRequest) return } // Check that request is in range if foreReq.Longitude > 180 || foreReq.Longitude < -180 { slog.Error("invalid request", "latitude", foreReq.Latitude, "longitude", foreReq.Longitude) http.Error(w, "invalid request", http.StatusBadRequest) return } // Call out to NOAA for forecast resp, err := noaa.Forecast(fmt.Sprintf("%f", foreReq.Latitude), fmt.Sprintf("%f", foreReq.Longitude)) if err != nil { slog.Error("noaa forecast request error", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } // Find today's forecast var result ForecastResp // SHORTCUT: assuming [0] is the closest to "today", and Periods has elements p := resp.Periods[0] result.ShortForecast = p.Summary switch { case p.Temperature < 32: result.Temperature = "cold" case p.Temperature < 70: result.Temperature = "moderate" default: result.Temperature = "hot" } // Return result w.WriteHeader(http.StatusOK) if err := json.NewEncoder(w).Encode(result); err != nil { slog.Error("encode forecast response error", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } }