Constantin Druccdruc

Hey, I'm Constantin Druc!

I'm a web developer sharing everything I know about building web applications.

I'm making a course on Laravel Sanctum: MasteringAuth.com

How to Use Golang to Check the Weather

In this post, we'll walk through how to build a CLI app using Golang that retrieves and displays the weather for a given location using the weatherapi.com API.

Step 1: Import Required Packages

First, we need to import the packages we'll need to interact with the API and process the data. We need to import encoding/json for JSON decoding, fmt for output, io for IO functions, net/http for HTTP requests, os for command-line arguments, and time for date and time handling. We'll also use the github.com/fatih/color package to add some color to our output.

package main

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"time"

	"github.com/fatih/color"
)

Step 2: Define Weather Struct

Next, we'll define a struct to hold the weather data retrieved from the API. We'll tag each field with their coresponding JSON property.

type Weather struct {
    Location struct {
        Name    string `json:"name"`
        Country string `json:"country"`
    } `json:"location"`
    Current struct {
        TempC     float64 `json:"temp_c"`
        Condition struct {
            Text string `json:"text"`
        } `json:"condition"`
    } `json:"current"`
    Forecast struct {
        Forecastday []struct {
            Hour []struct {
                TimeEpoch int64   `json:"time_epoch"`
                TempC     float64 `json:"temp_c"`
                Condition struct {
                    Text string `json:"text"`
                } `json:"condition"`
                ChanceOfRain float64 `json:"chance_of_rain"`
            } `json:"hour"`
        } `json:"forecastday"`
    } `json:"forecast"`
}

Step 3: Define Main Function

Now, we'll define the main function of our program. We'll start by setting a default location ("Iasi") in case no command-line arguments are provided. Then, we'll make an HTTP request to the weatherapi.com API to retrieve the weather data for the specified location. We'll then decode the JSON response and store it in our "weather" struct.

func main() {
    q := "Iasi"

    if len(os.Args) >= 2 {
        q = os.Args[1]
    }

    res, err := http.Get("http://api.weatherapi.com/v1/forecast.json?key=getYourOwnKey&q=" + q + "&days=1&aqi=no&alerts=no")
    if err != nil {
        panic(err)
    }
    defer res.Body.Close()

    if res.StatusCode != 200 {
        panic("Weather API not available")
    }

    body, err := io.ReadAll(res.Body)
    if err != nil {
        panic(err)
    }

    var weather Weather
    err = json.Unmarshal(body, &weather)
    if err != nil {
        panic(err)
    }
}

Step 4: Output Weather Data

With the weather data stored in our "weather" struct, we can now output it to the user. We'll start by extracting the relevant data and then output the current weather conditions.

location, current, hours := weather.Location, weather.Current, weather.Forecast.Forecastday[0].Hour

fmt.Printf("%s, %s: %.0fC, %s\n", location.Name, location.Country, current.TempC, current.Condition.Text)

Then we loop through the hours slice and print the forecast to the console. We use the time.Unix() function to convert the time epoch returned by the API to a date we can compare with the current time (time.Now()) such that we skip hours that are in the past. If the chances of rain are more than 40%, we print the output in red.

for _, hour := range hours {
    date := time.Unix(hour.TimeEpoch, 0)

    if date.Before(time.Now()) {
        continue
    }

    message := fmt.Sprintf("%s - %.0fC, %.0f%%, %s\n", date.Format("15:04"), hour.TempC, hour.ChanceOfRain, hour.Condition.Text)

    if hour.ChanceOfRain < 40 {
        fmt.Print(message)
    } else {
        color.Red(message)
    }
}

And that's it! We have created a simple Go program that retrieves and displays the weather forecast for the next few hours using the Weather API.

Final program:

package main

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"time"

	"github.com/fatih/color"
)

type Weather struct {
	Location struct {
		Name    string `json:"name"`
		Country string `json:"country"`
	} `json:"location"`
	Current struct {
		TempC     float64 `json:"temp_c"`
		Condition struct {
			Text string `json:"text"`
		} `json:"condition"`
	} `json:"current"`
	Forecast struct {
		Forecastday []struct {
			Hour []struct {
				TimeEpoch int64   `json:"time_epoch"`
				TempC     float64 `json:"temp_c"`
				Condition struct {
					Text string `json:"text"`
				} `json:"condition"`
				ChanceOfRain float64 `json:"chance_of_rain"`
			} `json:"hour"`
		} `json:"forecastday"`
	} `json:"forecast"`
}

func main() {
	q := "Iasi"

	if len(os.Args) >= 2 {
		q = os.Args[1]
	}

	res, err := http.Get("http://api.weatherapi.com/v1/forecast.json?key=ec180872243c4f57a4f153631230105&q=" + q + "&days=1&aqi=no&alerts=no")
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	if res.StatusCode != 200 {
		panic("Weather API not available")
	}

	body, err := io.ReadAll(res.Body)
	if err != nil {
		panic(err)
	}

	var weather Weather
	err = json.Unmarshal(body, &weather)
	if err != nil {
		panic(err)
	}

	location, current, hours := weather.Location, weather.Current, weather.Forecast.Forecastday[0].Hour

	fmt.Printf(
		"%s, %s: %.0fC, %s\n",
		location.Name,
		location.Country,
		current.TempC,
		current.Condition.Text,
	)

	for _, hour := range hours {
		date := time.Unix(hour.TimeEpoch, 0)

		if date.Before(time.Now()) {
			continue
		}

		message := fmt.Sprintf(
			"%s - %.0fC, %.0f%%, %s\n",
			date.Format("15:04"),
			hour.TempC,
			hour.ChanceOfRain,
			hour.Condition.Text,
		)

		if hour.ChanceOfRain < 40 {
			fmt.Print(message)
		} else {
			color.Red(message)
		}
	}
}
Tags: