Writing an HTTP Client in Go
Table of Contents
One of my new years resolutions was to learn new programming languages. Consequently, I recently set out to familiarize myself with Go. I’ve found that one of the best ways to learn a new programming language is to start with simple console programs, as well as re-implementing earlier projects. Therefore, I set out to write a console program in Go as a starting point.
Project Setup
The project will a simple CLI-based HTTP client that extracts some JSON from a REST API endpoint of the user’s choosing, and saves it as a .json
file to the local file system. I won’t go into how to setup a development environment for Go (I just used Docker, and VS Code integrations) here. However, we do need to create some files and directories first. To begin with, we need to create a bin/
directory for executables, and a data/
directory to save our data dumps in. To implement the actual program, we need two source files: main.go
and fetch.go
, where we will be writing the bulk of our logic. While it is not strictly necessary, we will also be adding a Makefile
to simplify program compilation and execution, with the following rules:
SHELL := /bin/bash
run:
go run main.go fetch.go
compile:
go build -o bin/
GOOS=windows GOARCH=amd64 go build -o bin/
GOOS=darwin GOARCH=amd64 go build -o bin/
The Logic
The function we will be using to both extract and dump our data will be in the fetch.go
file. The code is as follows:
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
)
func getData(endpoint string, filename string) {
// make a GET request to fetch the JSON data
res, err := http.Get(endpoint)
// Log any errors
if err != nil {
log.Fatal(err)
}
// store the response body in a variable
body, err := ioutil.ReadAll(res.Body)
if err != nil {
log.Fatal(err)
}
// Create a file to write to
file, err := os.Create("./data/" + filename + ".json")
// Write the JSON to an external file
file.WriteString(string(body))
if err != nil {
log.Fatal(err)
} else {
fmt.Println("Download complete. Check the 'data' directory.")
}
}
As you can see, the code is actually pretty straightforward. Not necessarily Python simple, but still quite intuitive.
Main
Our main file will actually need to include a little bit of logic as well.
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
func main() {
// Create a reader
reader := bufio.NewReader(os.Stdin)
fmt.Print("Enter a URL: ")
url, _ := reader.ReadString('\n')
// Remove the newline before passing into getData() function
formattedUrl := strings.TrimSuffix(url, "\n")
fmt.Print("Enter a file name (without a file extension): ")
filename, _ := reader.ReadString('\n')
formattedFilename := strings.TrimSuffix(filename, "\n")
getData(formattedUrl, formattedFilename)
// Prompt the user to press enter to exit
fmt.Print("Press enter to exit.")
reader.ReadString('\n')
}
This will allow a user to pass in necessary values for the parameters in fetch.go
via standard input. After our program has fulfilled its purpose, we then prompt the user to press enter to exit the program.
To run the program, we run either go run main.go fetch.go
or make run
from the terminal.
Conclusions
Although this is a rather simple program, it was a pleasant introduction to Go nonetheless, and has compelled me to explore the language so more. I’m already working on a different console program in Golang. At some point in the near future, I also hope to implement something more sophisticated, like a REST API.
You can find the code for the project here.