Weather CLI in Go - Part 1

February 02, 2025

I just started to learn a bit about Golang. Im mainly a frontend developer but recently I started to use Neovim as my code editor and for some reason Im starting to like more the use of the terminal for coding and decided continuying exploring. And one clear option is Go. So I decided to build something small but useful, then I found this YouTube video about building a CLI in Go for checking the weather.

Initialize project

# You change crisecheverria for whatever username you want in your github repo
go mod init crisecheverria/weather

This command creates a Go Module go.mod. It's a collection of Go packages with versioned dependencies.

Create a file main.go:

package main

import "fmt"

func main() {
    fmt.Println("Hello weather!")
}

You can test your code by running:

go run main.go

We're going to use Openweathermap API. You will need to create a variable WEATHERAPI_KEY with the value of the Openweather API KEY.

export WEATHERAPI_KEY=YOUR_KEY

Inside main.go

package main

import (
    "fmt"
    "os"
	"net/http"
)

func main() {
    apiKey := os.Getenv("WEATHERAPI_KEY")
	if apiKey == "" {
		fmt.Fprintln(os.Stderr, "Missing WEATHERAPI_KEY")
		os.Exit(1)
	}

	location := "Gothenburg"

	url := fmt.Sprintf("http://api.openweathermap.org/data/2.5/forecast?appid=%s&units=metric&q=%s", apiKey, location)

	res, err := http.Get(url)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error fetching weather data: %v\n", err)
		os.Exit(1)
	}
    	defer res.Body.Close()

    if res.StatusCode != http.StatusOK {
		fmt.Fprintf(os.Stderr, "Weather API returned non-200 status: %d\n", res.StatusCode)
		os.Exit(1)
	}

}

Some explanation:

    apiKey := os.Getenv("WEATHERAPI_KEY")
	if apiKey == "" {
		fmt.Fprintln(os.Stderr, "Missing WEATHERAPI_KEY")
		os.Exit(1)
	}

We check for the WEATHERAPI_KEY in our system. It it doesnt exists or if its empty we print a message to the user uaing system os.Stderr function and terminate program execution with os.Exit(1) you can also see that module os needs to be imported in order to use Stderr and Exit().


	location := "Gothenburg"

	url := fmt.Sprintf("http://api.openweathermap.org/data/2.5/forecast?appid=%s&units=metric&q=%s", apiKey, location)

	res, err := http.Get(url)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error fetching weather data: %v\n", err)
		os.Exit(1)
	}
    	defer res.Body.Close()

We define a local variable location and initialize it to Gothenburg for now, later we will read user input arguments. Then we store the url string injecting the apiKey and location. After that we use http module and make a Get call, this methos returns two values we store them inside res and err. Also check if err is diferent than nill, which would mean we have an error from the endpoint, if case we print it.

And finally we close the http.Get response with defer res.Body.Close() to esure that the HTTP response body is properly closed once it is no longer needed. This is critical in Go when working with HTTP to avoid resource leaks. By using defer the response body is closed after the program finishes processing the response.

	if res.StatusCode != http.StatusOK {
		fmt.Fprintf(os.Stderr, "Weather API returned non-200 status: %d\n", res.StatusCode)
		os.Exit(1)
	}

We check if the response status code is different than 200, if it is we print the status code and exit the program.

	body, err := io.ReadAll(res.Body)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error reading weather data: %v\n", err)
		os.Exit(1)
	}

	fmt.Println(string(body))

We read the response body and store it in body and check for errors. If there are no errors we print the body. Dont forget to add the io module to the imports.

import "io"

Now lets run our program again:

go run main.go

By default the OpenWeather API returns a JSON response with a lot of information. You can use Postman to test the API and see the response.

This is for now the end if the first part of the tutorial. In the next part we will parse the JSON response and display the weather information in a more readable format.

Stay tuned! 🚀

Full code so far:


package main

import (
	"fmt"
	"io"
	"net/http"
	"os"
)

func main() {
	apiKey := os.Getenv("WEATHERAPI_KEY")
	if apiKey == "" {
		fmt.Fprintln(os.Stderr, "Missing WEATHERAPI_KEY")
		os.Exit(1)
	}

	location := "Gothenburg"

	url := fmt.Sprintf("http://api.openweathermap.org/data/2.5/forecast?appid=%s&units=metric&q=%s", apiKey, location)

	res, err := http.Get(url)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error fetching weather data: %v\n", err)
		os.Exit(1)
	}
	defer res.Body.Close()

	body, err := io.ReadAll(res.Body)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error reading weather data: %v\n", err)
		os.Exit(1)
	}

	fmt.Println(string(body))
}