### Startup Configuration Example Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/configuration.md Demonstrates how config.Get() is called at application startup to retrieve the port configuration. The server is then initialized and started on the specified port. ```go port := config.Get().Port log.Println("Initialize server on port: " + port) if err := http.ListenAndServe(": ``` -------------------------------- ### Complete Cache Usage Example Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/api-cache.md Demonstrates the full lifecycle of a cache entry: getting the manager, creating a cache, storing data, retrieving it, and deleting it. Shows handling of cache hits and misses. ```go import ( "fmt" "time" "github.com/robertoduessmann/weather-api/cache" ) func main() { // Get or create cache manager cacheManager := cache.NewCacheManager() // Create a cache with 2-minute TTL weatherCache := cacheManager.NewCache("weather", 2*time.Minute) // Store a value cacheKey := "Berlin-m" jsonData := []byte(`{"temperature":"29 °C","wind":"20 km/h"}`) weatherCache.Put(cacheKey, jsonData) // Retrieve cached value if cached, found := weatherCache.Get(cacheKey); found { data := cached.([]byte) fmt.Println(string(data)) // {"temperature":"29 °C","wind":"20 km/h"} } // Delete from cache weatherCache.Delete(cacheKey) // Retrieve again (not found) if _, found := weatherCache.Get(cacheKey); !found { fmt.Println("Cache miss - key expired or deleted") } } ``` -------------------------------- ### Load Configuration and Start HTTP Server Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/architecture.md Shows how to retrieve application configuration, specifically the server port, which defaults to '3000' or can be set via the PORT environment variable. This configuration is used to start the HTTP listener. ```go cfg := config.Get() port := cfg.Port // Returns "3000" or custom PORT env var http.ListenAndServe(": ``` -------------------------------- ### API Usage Example Source: https://github.com/robertoduessmann/weather-api/blob/master/README.md Example of how to make a request to the weather API to get weather information for a specific city. ```sh curl http://localhost:3000/weather/{city} ``` -------------------------------- ### Install Project Dependencies Source: https://github.com/robertoduessmann/weather-api/blob/master/CONTRIBUTING.md Run this command in your terminal to install the necessary dependencies for the project. ```bash dep ensure ``` -------------------------------- ### Create and Marshal Weather Data Example Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/types.md Demonstrates how to create an instance of the `Weather` struct and marshal it into JSON format for an API response. This example shows the structure of the output. ```go weather := model.Weather{ Temperature: "29 °C", Wind: "20 km/h", Description: "Partly cloudy", Forecast: [3]model.Forecast{ {Day: "1", Temperature: "27 °C", Wind: "12 km/h"}, {Day: "2", Temperature: "22 °C", Wind: "8 km/h"}, {Day: "3", Temperature: "25 °C", Wind: "15 km/h"}, }, } data, _ := json.Marshal(weather) // {"temperature":"29 °C","wind":"20 km/h","description":"Partly cloudy","forecast":[...]} ``` -------------------------------- ### CurrentWeather V1 Usage Example Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/api-controller.md Example of how to register the CurrentWeather handler using gorilla/mux and client-side usage examples for the V1 API. ```go import "net/http" // This is registered in main.go with gorilla/mux weather := mux.NewRouter() weather.Path("/weather/{city}").Methods(http.MethodGet).HandlerFunc(controller.CurrentWeather) // Client usage: // GET http://localhost:3000/weather/Berlin // GET http://localhost:3000/weather/London // GET http://localhost:3000/weather/Curitiba ``` -------------------------------- ### Supported Cities Example Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/quick-start.md Demonstrates how to request weather data for various supported cities. ```APIDOC ## Supported Cities ### Description The API supports weather data for any city recognized by the wttr.in service. City names with spaces might require URL encoding. ### Method GET ### Endpoint Examples - `/weather/London` - `/weather/New_York` - `/weather/Tokyo` - `/weather/Sydney` - `/weather/Paris` ### Request Example (URL Encoding) `curl http://localhost:3000/weather/New%20York` ``` -------------------------------- ### Example Usage of Config.Get() Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/api-config.md Demonstrates how to load configuration using the `config.Get()` function and access the server port. This is typically done at application startup. ```go package main import ( "log" "github.com/robertoduessmann/weather-api/config" ) func main() { cfg := config.Get() log.Println("Server port:", cfg.Port) // Output: Server port: 3000 (or custom PORT value) } ``` -------------------------------- ### Create Forecast Example Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/types.md Example of initializing and marshaling a Forecast struct to JSON. Ensure the 'Day' field matches the API version's expected format (numeric index for V1, weekday name for V2). ```go forecast := model.Forecast{ Day: "Monday", Temperature: "27 °C", Wind: "12 km/h", } data, _ := json.Marshal(forecast) // {"day":"Monday","temperature":"27 °C","wind":"12 km/h"} ``` -------------------------------- ### Get Configuration Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/INDEX.md Retrieves the application's configuration, primarily used to access the server port setting. ```go import "github.com/robertoduessmann/weather-api/config" cfg := config.Get() // Get() -> Config port := cfg.Port // "3000" ``` -------------------------------- ### Example 404 Error Request Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/errors.md Demonstrates how to trigger a 404 Not Found error by requesting weather data for an invalid city. ```bash curl http://localhost:3000/v2/weather/InvalidCityXYZ # Response: 404 Not Found # Body: {"message":"NOT_FOUND"} ``` -------------------------------- ### Use Configured Port in Server Initialization Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/api-config.md Illustrates how to retrieve the configured port from the `config.Get()` function and use it to start the HTTP server. The port is prepended with a colon to match the expected format for `http.ListenAndServe`. ```go package main import ( "log" "net/http" "github.com/gorilla/handlers" "github.com/gorilla/mux" "github.com/robertoduessmann/weather-api/config" "github.com/robertoduessmann/weather-api/controller" ) func main() { // Load configuration cfg := config.Get() // Create router weather := mux.NewRouter() // Register handlers weather.Path("/weather/{city}").Methods(http.MethodGet).HandlerFunc(controller.CurrentWeather) // Start server on configured port log.Println("Initialize server on port: " + cfg.Port) if err := http.ListenAndServe(":"+cfg.Port, handlers.CORS()(weather)); err != nil { log.Fatal(err) } } ``` -------------------------------- ### Registering V2 Weather Routes Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/api-controller.md Example of how to register the V2 weather endpoint handlers using gorilla/mux. Includes routes for both with and without the optional unit query parameter. ```go import "net/http" // These are registered in main.go with gorilla/mux weather := mux.NewRouter() weather.Path("/v2/weather/{city}").Queries("unit", "{unit}").Methods(http.MethodGet).HandlerFunc(v2.CurrentWeather) weather.Path("/v2/weather/{city}").Methods(http.MethodGet).HandlerFunc(v2.CurrentWeather) // Client usage: // GET http://localhost:3000/v2/weather/Berlin (default metric) // GET http://localhost:3000/v2/weather/Berlin?unit=m (explicit metric) // GET http://localhost:3000/v2/weather/Berlin?unit=u (imperial) ``` -------------------------------- ### Curl Example - Metric Units (Default) Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/endpoints.md Demonstrates fetching weather data for Berlin using default metric units via curl. ```bash curl http://localhost:3000/v2/weather/Berlin ``` -------------------------------- ### Success Response (Metric) Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/endpoints.md Example of a successful JSON response for current weather and a 3-day forecast using metric units. ```json { "temperature": "29 °C", "wind": "20 km/h", "description": "Partly cloudy", "forecast": [ { "day": "Monday", "temperature": "27 °C", "wind": "12 km/h" }, { "day": "Tuesday", "temperature": "22 °C", "wind": "8 km/h" }, { "day": "Wednesday", "temperature": "25 °C", "wind": "15 km/h" } ] } ``` -------------------------------- ### GET /weather/{city} Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/endpoints.md Retrieves current weather and a 3-day forecast for a specified city by scraping the wttr.in service. ```APIDOC ## GET /weather/{city} ### Description Returns current weather and 3-day forecast for a given city using the wttr.in service HTML scraping backend. ### Method GET ### Endpoint /weather/{city} ### Parameters #### Path Parameters - **city** (string) - Required - City name to fetch weather for (e.g., "Berlin", "Curitiba") ### Request Example ```json { "example": "request body" } ``` ### Response #### Success Response (200) - **temperature** (string) - Current temperature with unit (e.g., "29 °C") - **wind** (string) - Current wind speed with unit (e.g., "20 km/h") - **description** (string) - Weather description (e.g., "Partly cloudy") - **forecast** (Forecast[3]) - Array of exactly 3 forecast entries for following days **Forecast Object Schema:** - **day** (string) - Forecast day number (1-indexed: "1", "2", "3") - **temperature** (string) - Forecasted temperature with unit (e.g., "27 °C") - **wind** (string) - Forecasted wind speed with unit (e.g., "12 km/h") #### Response Example ```json { "temperature": "29 °C", "wind": "20 km/h", "description": "Partly cloudy", "forecast": [ { "day": "1", "temperature": "27 °C", "wind": "12 km/h" }, { "day": "2", "temperature": "22 °C", "wind": "8 km/h" }, { "day": "3", "temperature": "25 °C", "wind": "15 km/h" } ] } ``` #### Error Response (404 Not Found) - **message** (string) - Error message (e.g., "NOT_FOUND") #### Error Response Example ```json { "message": "NOT_FOUND" } ``` ``` -------------------------------- ### Curl Example - Metric Units (Explicit) Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/endpoints.md Demonstrates fetching weather data for Berlin using explicitly set metric units via curl. ```bash curl http://localhost:3000/v2/weather/Berlin?unit=m ``` -------------------------------- ### 404 Not Found HTTP Response Example Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/errors.md Illustrates the expected HTTP response for a 404 Not Found error, including the status line and JSON body. ```HTTP HTTP/1.1 404 Not Found Content-Type: application/json {"message":"NOT_FOUND"} ``` -------------------------------- ### Curl Example - Imperial Units Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/endpoints.md Demonstrates fetching weather data for Berlin using imperial units via curl. ```bash curl http://localhost:3000/v2/weather/Berlin?unit=u ``` -------------------------------- ### Debug Log Output Examples Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/quick-start.md These log messages indicate cache status (hit/miss) and warnings from the weather API. ```text [CACHE HIT] key=Berlin-m [CACHE MISS] key=Berlin-m Warning: weather API for InvalidCity returned status 404 ``` -------------------------------- ### Get Configuration at Runtime Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/configuration.md Retrieves the application's configuration using the config.Get() function. This function parses environment variables into the Config struct. Use the returned port to configure the HTTP server. ```go package main import ( "github.com/robertoduessmann/weather-api/config" ) func main() { cfg := config.Get() port := cfg.Port // e.g., "3000" or custom value from PORT env var // Use port to configure server http.ListenAndServe(": ``` -------------------------------- ### Example Usage of Weather Endpoint Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/endpoints.md Demonstrates how to call the /weather/{city} endpoint using curl for different cities. Ensure the API is running on http://localhost:3000. ```bash curl http://localhost:3000/weather/Berlin curl http://localhost:3000/weather/Curitiba curl http://localhost:3000/weather/London ``` -------------------------------- ### Run Weather API with Custom Port Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/README.md Starts the weather API application with a specified port. Ensure the PORT environment variable is set before execution. ```bash PORT=8080 ./weather-api ``` -------------------------------- ### Start Weather API Server Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/INDEX.md Build and run the Weather API application. You can use the default port 3000 or specify a custom port using the PORT environment variable. ```bash # Default port 3000 go build && ./weather-api ``` ```bash # Custom port PORT=8080 go build && ./weather-api ``` -------------------------------- ### Run Server on Different Port Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/quick-start.md If port 3000 is unavailable, you can start the server on a different port by setting the PORT environment variable. ```bash # Try different port PORT=8080 ./weather-api ``` -------------------------------- ### Check Port Availability Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/quick-start.md Use this command to check if port 3000 is currently in use before starting the server. ```bash # Check port is available lsof -i :3000 ``` -------------------------------- ### Go Client Example for Weather API Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/quick-start.md Fetch weather data from V1 and V2 endpoints using the Go HTTP client. Includes JSON unmarshalling into a Weather struct. ```go package main import ( "encoding/json" "io" "net/http" ) type Weather struct { Temperature string `json:"temperature"` Wind string `json:"wind"` Description string `json:"description"` Forecast []struct { Day string `json:"day"` Temperature string `json:"temperature"` Wind string `json:"wind"` } `json:"forecast"` } func main() { // V1 API resp, _ := http.Get("http://localhost:3000/weather/Berlin") body, _ := io.ReadAll(resp.Body) var weather Weather json.Unmarshal(body, &weather) println(weather.Temperature) // V2 API with units resp, _ = http.Get("http://localhost:3000/v2/weather/Berlin?unit=u") body, _ = io.ReadAll(resp.Body) json.Unmarshal(body, &weather) println(weather.Temperature) } ``` -------------------------------- ### Current Weather Response Source: https://github.com/robertoduessmann/weather-api/blob/master/README.md Example JSON response from the weather API for a city, including temperature, wind, and description. ```json { "temperature": "29 °C", "wind": "20 km/h", "description": "Partly cloudy", "forecast": [ { "day": "1", "temperature": "27 °C", "wind": "12 km/h" }, { "day": "2", "temperature": "22 °C", "wind": "8 km/h" } ] } ``` -------------------------------- ### Example cURL Request for Invalid City Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/errors.md Demonstrates how to trigger a 404 Not Found error by requesting weather for a non-existent city using cURL. ```Bash curl http://localhost:3000/weather/InvalidCityXYZ # Response: 404 Not Found # Body: {"message":"NOT_FOUND"} ``` -------------------------------- ### Python Client Example for Weather API Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/quick-start.md Fetch weather data from V1 and V2 endpoints using the Python requests library. Parses JSON responses. ```python import requests # V1 API resp = requests.get('http://localhost:3000/weather/Berlin') weather = resp.json() print(weather['temperature']) # V2 API with units resp = requests.get('http://localhost:3000/v2/weather/Berlin', params={'unit': 'u'}) weather = resp.json() print(weather['temperature']) ``` -------------------------------- ### Example Usage of Parse Function Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/api-parser.md Demonstrates how to use the parser.Parse function to extract temperature, wind speed, and descriptive text from a goquery document using different CSS selector arrays. ```go import ( "github.com/PuerkitoBio/goquery" "github.com/robertoduessmann/weather-api/parser" ) // Assume doc is *goquery.Document from wttr.in HTML response // Extract temperature from one of two possible selectors temperatureTags := []string{ "body > pre > span:nth-child(3)", "body > pre > span:nth-child(2)", } temperature := parser.Parse(doc, temperatureTags) // Result: "29" // Extract wind speed windTags := []string{ "body > pre > span:nth-child(6)", "body > pre > span:nth-child(7)", } wind := parser.Parse(doc, windTags) // Result: "20" // Extract description (special case) descriptionTags := []string{"body > pre"} description := parser.Parse(doc, descriptionTags) // Result: "Partly cloudy" ``` -------------------------------- ### Get Configuration Function Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/api-config.md Retrieves and parses configuration from environment variables into the Config struct. It logs a fatal error and exits if parsing fails, ensuring that configuration is always valid before proceeding. ```go func Get() (cfg Config) { err := env.Parse(&cfg) if err != nil { log.Fatal(err) } return } ``` -------------------------------- ### Register Weather API Routes (Go) Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/api-controller.md Register the weather API endpoints using `mux.NewRouter`. This example shows how to define paths, HTTP methods, query parameters, and handler functions, including CORS middleware. ```go weather := mux.NewRouter() weather. Path("/weather/{city}") Methods(http.MethodGet) HandlerFunc(controller.CurrentWeather) weather. Path("/v2/weather/{city}") Queries("unit", "{unit}") Methods(http.MethodGet) HandlerFunc(v2.CurrentWeather) weather. Path("/v2/weather/{city}") Methods(http.MethodGet) HandlerFunc(v2.CurrentWeather) http.ListenAndServe(":"+port, handlers.CORS()(weather)) ``` -------------------------------- ### JavaScript/Node.js Client Example for Weather API Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/quick-start.md Fetch weather data from V1 and V2 endpoints using the JavaScript fetch API. Handles JSON response parsing. ```javascript // V1 API fetch('http://localhost:3000/weather/Berlin') .then(r => r.json()) .then(weather => console.log(weather.temperature)); // V2 API with units fetch('http://localhost:3000/v2/weather/Berlin?unit=u') .then(r => r.json()) .then(weather => console.log(weather.temperature)); ``` -------------------------------- ### Handle API Errors with ErrorMessage Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/types.md Example of creating and marshaling an ErrorMessage, and then writing it to an HTTP response with an appropriate status code. This is useful for indicating issues like 'NOT_FOUND' or server-side problems. ```go errMsg := model.ErrorMessage{Message: "NOT_FOUND"} data, _ := json.Marshal(errMsg) // {"message":"NOT_FOUND"} w.WriteHeader(http.StatusNotFound) json.NewEncoder(w).Encode(errMsg) ``` -------------------------------- ### GET /v2/weather/{city} Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/endpoints.md Retrieves the current weather and a 3-day forecast for a specified city. Supports unit selection for metric or imperial measurements. ```APIDOC ## GET /v2/weather/{city} ### Description Returns current weather and 3-day forecast with support for unit selection (Celsius/Metric or Fahrenheit/Imperial). ### Method GET ### Endpoint /v2/weather/{city} ### Parameters #### Path Parameters - **city** (string) - Required - City name to fetch weather for (e.g., "Berlin", "London") #### Query Parameters - **unit** (string) - Optional - Unit system: "m" for metric (°C, km/h), "u" for imperial (°F, mph). Defaults to "m" (metric). ### Response #### Success Response (200 OK) - **temperature** (string) - Current temperature with unit (selected by unit param) - **wind** (string) - Current wind speed with unit (selected by unit param) - **description** (string) - Weather description - **forecast** (Forecast[3]) - Array of exactly 3 forecast entries for following days **Forecast Object Schema:** - **day** (string) - Forecast day name (e.g., "Monday", "Tuesday", "Wednesday") - **temperature** (string) - Forecasted temperature with unit (selected by unit param) - **wind** (string) - Forecasted wind speed with unit (selected by unit param) #### Response Example { "temperature": "29 °C", "wind": "20 km/h", "description": "Partly cloudy", "forecast": [ { "day": "Monday", "temperature": "27 °C", "wind": "12 km/h" }, { "day": "Tuesday", "temperature": "22 °C", "wind": "8 km/h" }, { "day": "Wednesday", "temperature": "25 °C", "wind": "15 km/h" } ] } #### Error Response (404 Not Found) { "message": "NOT_FOUND" } #### Error Response (500 Internal Server Error) { "message": "error description" } ``` -------------------------------- ### Run on Custom Ports Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/configuration.md Examples of running the weather-api application on different ports by setting the PORT environment variable. If PORT is not set, the application defaults to port 3000. ```bash # Run on port 8080 PORT=8080 ./weather-api # Run on port 5000 PORT=5000 ./weather-api # Run on default port 3000 ./weather-api ``` -------------------------------- ### Curl Example - Imperial Units (London) Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/endpoints.md Demonstrates fetching weather data for London using imperial units via curl, with no trailing slash on the city parameter. ```bash curl http://localhost:3000/v2/weather/London?unit=u ``` -------------------------------- ### Fatal Error Handling in Config.Get() Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/api-config.md Shows the error handling within the `Get` function, where any parsing error from `env.Parse` results in a call to `log.Fatal(err)`, terminating the application. This ensures that the application does not run with invalid configuration. ```go func Get() (cfg Config) { err := env.Parse(&cfg) if err != nil { log.Fatal(err) // Terminates application } return } ``` -------------------------------- ### Create New Cache Client Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/api-cache.md Use NewCacheClient to create a new CacheClient with a specified TTL. This function initializes an in-memory map, starts a background goroutine for cleanup, and returns the CacheClient interface. ```go func NewCacheClient(ttl time.Duration) CacheClient ``` -------------------------------- ### V1: GET /weather/{city} Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/README.md Retrieves current weather information for a specified city. Supports basic temperature and wind speed in Celsius and km/h. Data is cached for 2 minutes per city. ```APIDOC ## GET /weather/{city} ### Description Retrieves current weather information for a specified city, including temperature in Celsius and wind speed in km/h. The response includes a forecast for the next 3 days. ### Method GET ### Endpoint /weather/{city} ### Parameters #### Path Parameters - **city** (string) - Required - The name of the city for which to retrieve weather data. ### Response #### Success Response (200) - **temperature** (number) - Weather in Celsius. - **wind_speed** (number) - Wind speed in km/h. - **forecast** (object) - Forecast for the next 3 days. Format: "1", "2", "3". ``` -------------------------------- ### Get Weather Data from API Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/INDEX.md Make requests to the Weather API to retrieve weather information for a specified city. Supports V1 and V2 APIs with options for metric or imperial units. ```bash # V1 API curl http://localhost:3000/weather/Berlin ``` ```bash # V2 API (metric) curl http://localhost:3000/v2/weather/Berlin?unit=m ``` ```bash # V2 API (imperial) curl http://localhost:3000/v2/weather/Berlin?unit=u ``` -------------------------------- ### V2: GET /v2/weather/{city}?unit=... Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/README.md Retrieves current weather information for a specified city with selectable units. Supports Celsius/km/h or Fahrenheit/mph. Provides a forecast for the next 7 days. Data is cached for 2 minutes per city and unit pair. ```APIDOC ## GET /v2/weather/{city}?unit=... ### Description Retrieves current weather information for a specified city with selectable units. Supports temperature and wind speed in either Celsius/km/h or Fahrenheit/mph. Includes a forecast for the next 7 days. ### Method GET ### Endpoint /v2/weather/{city} ### Parameters #### Path Parameters - **city** (string) - Required - The name of the city for which to retrieve weather data. #### Query Parameters - **unit** (string) - Optional - Specifies the unit system. Accepts "m" for Celsius/km/h (default) or "u" for Fahrenheit/mph. ### Response #### Success Response (200) - **temperature** (number) - Weather in the selected unit (°C or °F). - **wind_speed** (number) - Wind speed in the selected unit (km/h or mph). - **forecast** (object) - Forecast for the next 7 days. Format: "Monday", "Tuesday", etc. ``` -------------------------------- ### V2 Error Handling Pattern: Cache, Fetch JSON, Validate, Transform, Return Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/errors.md This Go code outlines the V2 error handling approach, starting with cache checks, fetching JSON data, validating the response status and content, transforming it, and finally returning the processed data or an error. ```go // Check cache first if cached, found := weatherCache.Get(cacheKey); found { w.Write(cached.([]byte)) return } // Fetch JSON res, err := http.Get(uri) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } // Validate status if res.StatusCode != http.StatusOK { writeError(w, http.StatusNotFound, "NOT_FOUND") return } // Decode and validate data if err := json.NewDecoder(res.Body).Decode(&wttr); err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } if len(wttr.CurrentCondition) == 0 || len(wttr.CurrentCondition[0].WeatherDesc) == 0 { writeError(w, http.StatusInternalServerError, "Unexpected API response") return } // Build response with unit conversion response := model.Weather{...} // Return success encoded, err := json.Marshal(response) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } weatherCache.Put(cacheKey, encoded) w.Write(encoded) ``` -------------------------------- ### V2 API - Get Weather with Unit Selection Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/quick-start.md Request weather data using the V2 JSON backend. Supports metric (default) and imperial units via the `unit` query parameter. Handles successful requests and city not found errors. ```bash # Metric units (Celsius, km/h) - default curl http://localhost:3000/v2/weather/Berlin # Metric units (explicit) curl http://localhost:3000/v2/weather/Berlin?unit=m # Imperial units (Fahrenheit, mph) curl http://localhost:3000/v2/weather/Berlin?unit=u # Response (200 OK) { "temperature": "29 °C", "wind": "20 km/h", "description": "Partly cloudy", "forecast": [ {"day": "Monday", "temperature": "27 °C", "wind": "12 km/h"}, {"day": "Tuesday", "temperature": "22 °C", "wind": "8 km/h"}, {"day": "Wednesday", "temperature": "25 °C", "wind": "15 km/h"} ] } # City not found (404) curl http://localhost:3000/v2/weather/InvalidCity?unit=m # Response: {"message":"NOT_FOUND"} ``` -------------------------------- ### Get Current Weather Source: https://github.com/robertoduessmann/weather-api/blob/master/README.md Retrieves the current weather conditions for a given city. This endpoint is accessible via HTTP GET requests. ```APIDOC ## GET /weather/{city} ### Description Retrieves the current weather conditions for a specified city. ### Method GET ### Endpoint /weather/{city} ### Parameters #### Path Parameters - **city** (string) - Required - The name of the city for which to retrieve weather information. ### Request Example ```sh curl http://localhost:3000/weather/Curitiba ``` ### Response #### Success Response (200) - **temperature** (string) - The current temperature in the city. - **wind** (string) - The current wind speed in the city. - **description** (string) - A brief description of the current weather conditions. - **forecast** (array) - An array of forecast data for the upcoming days. - **day** (string) - The day number for the forecast. - **temperature** (string) - The forecasted temperature for that day. - **wind** (string) - The forecasted wind speed for that day. #### Response Example ```json { "temperature": "29 °C", "wind": "20 km/h", "description": "Partly cloudy", "forecast": [ { "day": "1", "temperature": "27 °C", "wind": "12 km/h" }, { "day": "2", "temperature": "22 °C", "wind": "8 km/h" } ] } ``` ``` -------------------------------- ### Build the Weather API Application Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/quick-start.md Navigate to the application directory and build the Go executable. This creates the `weather-api` binary in the current directory. ```bash cd weather-api go build ``` -------------------------------- ### Parser Usage Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/INDEX.md Demonstrates how to use the parser package to parse content using specified selectors. ```go import "github.com/robertoduessmann/weather-api/parser" value := parser.Parse(doc, []string{"selector1", "selector2"}) ``` -------------------------------- ### Model Weather Struct Initialization Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/INDEX.md Shows how to initialize the Weather struct from the model package with sample data for temperature, wind, description, and forecast. ```go import "github.com/robertoduessmann/weather-api/model" weather := model.Weather{ Temperature: "29 °C", Wind: "20 km/h", Description: "Partly cloudy", Forecast: [3]model.Forecast{...}, } ``` -------------------------------- ### Server Error Response Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/endpoints.md Example of a JSON response indicating a server error. ```json { "message": "error description" } ``` -------------------------------- ### Initialize and Use Cache Client Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/architecture.md Demonstrates how to create a cache manager, obtain a named cache with a specific TTL, retrieve data from the cache, and store data if not found. This pattern is used in controllers to reduce redundant data fetching. ```go cacheManager := cache.NewCacheManager() weatherCache := cacheManager.NewCache("weather", 2*time.Minute) if cached, found := weatherCache.Get(key); found { return cached } // ... fetch and process data ... weatherCache.Put(key, result) ``` -------------------------------- ### Request Current Weather Source: https://github.com/robertoduessmann/weather-api/blob/master/README.md Example cURL command to fetch the current weather for Curitiba. ```sh curl http://localhost:3000/weather/Curitiba ``` -------------------------------- ### Cache Manager and Client Usage Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/INDEX.md Demonstrates creating a cache manager and then a specific cache client for storing and retrieving data with a defined expiration. ```go import "github.com/robertoduessmann/weather-api/cache" manager := cache.NewCacheManager() // Singleton client := manager.NewCache("weather", 2*time.Minute) client.Put("berlin", data) value, found := client.Get("berlin") ``` -------------------------------- ### Build Project Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/quick-start.md Ensure the project builds successfully with verbose output to identify any compilation issues. ```bash # Check go build succeeded go build -v ``` -------------------------------- ### Run the API Source: https://github.com/robertoduessmann/weather-api/blob/master/README.md Command to execute the built weather-api application. ```sh ./weather-api ``` -------------------------------- ### Get Method Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/api-cache.md Retrieves a value from cache by key. Returns the value and a boolean indicating if the key was found and not expired. Thread-safe. ```APIDOC ## Get Method ### Description Retrieves a value from cache by key. Returns the value and a boolean indicating if the key was found and not expired. Thread-safe. ### Method GET (conceptual) ### Endpoint N/A (SDK method) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```go if cached, found := weatherCache.Get("Berlin-m"); found { data := cached.([]byte) w.Write(data) } ``` ### Response #### Success Response - **value** (any) - The cached value if found and not expired, otherwise nil - **found** (bool) - True if key exists and value is not expired, false otherwise #### Response Example (nil, false) if key does not exist or has expired (value, true) if key exists and not expired ``` -------------------------------- ### Singleton Cache Manager Instance Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/api-cache.md Ensures only one instance of the CacheManager exists application-wide. Call NewCacheManager() to get the singleton instance. ```go func NewCacheManager() CacheManager { once.Do(func() { instance = &cacheManager{ avaliablesCache: make(map[string]CacheClient), } }) return instance } ``` -------------------------------- ### CacheClient Interface Definition Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/api-cache.md Defines the contract for a thread-safe cache client, specifying methods for getting, putting, and deleting key-value pairs. ```go type CacheClient interface { Get(key string) (any, bool) Put(key string, value any) bool Delete(key string) bool } ``` -------------------------------- ### Application Initialization Flow Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/architecture.md Details the step-by-step process of initializing the weather-api application. This flow covers router creation, route registration for both V1 and V2 APIs, configuration loading, middleware application, and server startup. ```text main() ├─ Create gorilla/mux router ├─ Register GET /weather/{city} → controller.CurrentWeather (V1) ├─ Register GET /v2/weather/{city}?unit=... → v2.CurrentWeather (V2) ├─ Register GET /v2/weather/{city} → v2.CurrentWeather (V2) ├─ Call config.Get() → Load PORT from environment (default: 3000) ├─ Wrap router with CORS middleware (gorilla/handlers) └─ Start HTTP server on configured port ``` -------------------------------- ### Simulate Startup Fatal Error with Invalid Port Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/errors.md This bash command demonstrates how the application behaves when an invalid port is provided via an environment variable, showing the resulting error message and exit code. ```bash PORT=invalid_port ./weather-api # Output: 2024/01/01 12:00:00 strconv.ParseInt: parsing "invalid_port": invalid syntax # Exit code: 1 ``` -------------------------------- ### Build Locally (Mac) Source: https://github.com/robertoduessmann/weather-api/blob/master/README.md Command to build the weather-api project locally on Mac systems. ```sh go build ``` -------------------------------- ### CurrentWeather V1 Handler Function Signature Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/api-controller.md The function signature for the CurrentWeather handler in the V1 API. This handler processes GET requests to /weather/{city}. ```go func CurrentWeather(w http.ResponseWriter, r *http.Request) ``` -------------------------------- ### Get Value from Cache Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/api-cache.md Retrieves a value from the cache using its key. Returns the value and a boolean indicating if it was found and not expired. Ensure the cache is initialized before use. ```go func (c *cacheClient) Get(key string) (any, bool) ``` ```go cacheManager := cache.NewCacheManager() weatherCache := cacheManager.NewCache("weather", 2*time.Minute) if cached, found := weatherCache.Get("Berlin-m"); found { data := cached.([]byte) w.Write(data) } ``` -------------------------------- ### Run the Weather API Application Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/quick-start.md Execute the built application. It defaults to port 3000, but a custom port can be specified using the PORT environment variable. ```bash # Default port 3000 ./weather-api # Custom port PORT=8080 ./weather-api ``` -------------------------------- ### V1 Error Handling Pattern: Cache, Fetch, Parse, Return Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/errors.md This Go code illustrates the V1 error handling strategy, prioritizing cache checks, then fetching external data, parsing HTML responses, and returning either the data or a 'NOT_FOUND' error. ```go // Check cache first if cached, found := weatherCache.Get(cacheKey); found { w.Write(cached.([]byte)) return } // Fetch external data resp := getExternalWeather(city) if resp == nil { // Handle network/not found w.WriteHeader(http.StatusNotFound) fmt.Fprintf(w, string(toJSON(model.ErrorMessage{Message: "NOT_FOUND"}))) return } // Parse HTML if resp.StatusCode == 200 { err = parse(resp, &weather) } // Return result or error if err != nil { w.WriteHeader(http.StatusNotFound) fmt.Fprintf(w, string(toJSON(model.ErrorMessage{Message: "NOT_FOUND"}))) } else { weatherCache.Put(cacheKey, result) w.Write(result) } ``` -------------------------------- ### Using Configured Port in http.ListenAndServe Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/api-config.md Demonstrates the specific usage of the retrieved port string from the configuration within the `http.ListenAndServe` function. The port is concatenated with a colon prefix, as required by the function signature for binding to a specific port. ```go port := config.Get().Port log.Println("Initialize server on port: " + port) if err := http.ListenAndServe(":"+port, handlers.CORS()(weather)); err != nil { log.Fatal(err) } ``` -------------------------------- ### Controller HTTP Handlers Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/INDEX.md Illustrates how to use controller functions as HTTP handlers for both V1 and V2 of the weather API. ```go import "github.com/robertoduessmann/weather-api/controller" import v2 "github.com/robertoduessmann/weather-api/controller/v2" // Used as HTTP handlers controller.CurrentWeather(w, r) // V1 v2.CurrentWeather(w, r) // V2 ``` -------------------------------- ### Project Structure Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/architecture.md Illustrates the directory layout and key files within the weather-api project. This structure organizes code by functionality, including entry points, configuration, controllers, models, caching, and parsing utilities. ```text weather-api/ ├── main.go # Application entry point, router setup, server startup ├── go.mod # Go module dependencies ├── config/ │ └── config.go # Config struct and Get() function ├── controller/ │ ├── weather.go # V1 API handler (HTML scraping) │ └── v2/ │ ├── weather.go # V2 API handler (JSON API) │ └── model.go # Internal V2 data structures ├── model/ │ └── weather.go # Weather, Forecast, ErrorMessage types ├── cache/ │ ├── interface.go # CacheClient, CacheManager interfaces │ ├── client.go # CacheClient implementation │ └── manager.go # CacheManager singleton implementation └── parser/ └── parse.go # CSS selector-based content extraction ``` -------------------------------- ### Parse Temperature from HTML Document Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/architecture.md Illustrates the usage of the parser for V1 controllers, which extracts temperature data from an HTML document using CSS selectors. It provides alternative selectors for robustness. ```go temperature := parser.Parse(doc, []string{ "body > pre > span:nth-child(3)", "body > pre > span:nth-child(2)", }) ``` -------------------------------- ### V1 API - Get Current Weather and Forecast Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/quick-start.md Use curl to request current weather and forecast data for a specified city using the V1 HTML scraping backend. Handles both successful requests and city not found errors. ```bash # Current weather and forecast curl http://localhost:3000/weather/Berlin # Response (200 OK) { "temperature": "29 °C", "wind": "20 km/h", "description": "Partly cloudy", "forecast": [ {"day": "1", "temperature": "27 °C", "wind": "12 km/h"}, {"day": "2", "temperature": "22 °C", "wind": "8 km/h"}, {"day": "3", "temperature": "25 °C", "wind": "15 km/h"} ] } # City not found (404) curl http://localhost:3000/weather/InvalidCity # Response: {"message":"NOT_FOUND"} ``` -------------------------------- ### Cache Item Structure and Expiration Check Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/api-cache.md Defines the item struct for cache entries, including their value and expiry time. The isExpired method checks if a cache entry has passed its expiry time. Expiration is checked on Get() calls and expired entries are removed by a background cleanup goroutine. ```go type item[V any] struct { value V expiry time.Time } func (i item[V]) isExpired() bool { return time.Now().After(i.expiry) } ``` -------------------------------- ### Set Custom Port via Environment Variable Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/api-config.md Shows how to override the default server port by setting the PORT environment variable before running the application. This allows for flexible deployment configurations. ```bash # Default port 3000 ./weather-api # Custom port 8080 PORT=8080 ./weather-api # Custom port 5000 PORT=5000 ./weather-api ``` -------------------------------- ### Querying Supported Cities in Weather API Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/quick-start.md These curl commands show how to request weather data for various cities. Ensure city names with spaces are properly URL-encoded if necessary. ```bash curl http://localhost:3000/weather/London curl http://localhost:3000/weather/New_York curl http://localhost:3000/weather/Tokyo curl http://localhost:3000/weather/Sydney curl http://localhost:3000/weather/Paris ``` -------------------------------- ### Check Local API Request Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/quick-start.md Verify that the request to your local weather API is correctly formatted. ```bash # Check request is correct curl http://localhost:3000/weather/Berlin ``` -------------------------------- ### Fetch Weather for Multiple Cities Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/quick-start.md Iterate over a list of cities to fetch weather data for each one using the V2 endpoint. Ensure proper response processing. ```go cities := []string{"London", "Paris", "Berlin", "Madrid"} for _, city := range cities { resp, _ := http.Get(fmt.Sprintf("http://localhost:3000/v2/weather/%s", city)) // Process response } ``` -------------------------------- ### Handling 404 Not Found Error in Go Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/errors.md Shows the Go code implementation for returning a 404 Not Found error response when a city is not found or data is missing. ```Go // In CurrentWeather() resp := getExternalWeather(city) if resp == nil { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusNotFound) fmt.Fprintf(w, string(toJSON(model.ErrorMessage{Message: "NOT_FOUND"}))) return } ``` ```Go // In parse() if notFound(weather) { return errors.New("NOT_FOUND") } ``` ```Go // In CurrentWeather() if err != nil { w.WriteHeader(http.StatusNotFound) fmt.Fprintf(w, string(toJSON(model.ErrorMessage{Message: "NOT_FOUND"}))) } ``` -------------------------------- ### Parse Weather Data from HTML Source: https://github.com/robertoduessmann/weather-api/blob/master/_autodocs/api-parser.md This Go code snippet demonstrates how to use the parser to extract weather information from an HTML document. It shows how to handle multiple CSS selectors for a single data point and includes basic validation. ```go package controller import ( "github.com/PuerkitoBio/goquery" "github.com/robertoduessmann/weather-api/model" "github.com/robertoduessmann/weather-api/parser" ) func parseWeatherFromHTML(doc *goquery.Document) (*model.Weather, error) { weather := &model.Weather{} // Extract current conditions weather.Temperature = parser.Parse(doc, []string{ "body > pre > span:nth-child(3)", "body > pre > span:nth-child(2)", }) if len(weather.Temperature) > 0 { weather.Temperature += " °C" } weather.Wind = parser.Parse(doc, []string{ "body > pre > span:nth-child(6)", "body > pre > span:nth-child(7)", }) if len(weather.Wind) > 0 { weather.Wind += " km/h" } weather.Description = parser.Parse(doc, []string{ "body > pre", }) // Validate parsing succeeded if len(weather.Description) == 0 { return nil, errors.New("failed to parse weather data") } // Extract forecasts... return weather, nil } ```