How to create a CLI app with Go
Go provides lot of tools for building great CLI apps. Some of the great tools available in the go ecosystem are tools like Cobra, Bubbletea. In this tutorial we will look at how we can build a command line app in Cobra.
Create a new go project
Start by creating a new project folder in go. Create the main.go file.
mkdir <go-project-name>
cd <go-project-name>
touch main.go
Create a main.go file
A main.go file will be the entry point of your application. Create a main.go file in the root of your project and run go run main.go to check if everything is working fine.
package main
import "fmt"
func main(){
fmt.Println("Hello")
}
Create a mod file
Let's begin by initializing a Go module. The go.mod
file serves a purpose similar to package.json
in JavaScript—it provides a blueprint of all the dependencies your application will use, ensuring a well-defined structure for package management.
go mod init example.com/cliapp
Add the latest Cobra package
You're now ready to install the Cobra CLI framework. Simply run the following command in your terminal to get it set up
go get -u github.com/spf13/cobra@latest
Install Cobra CLI
To use Cobra's commands, you'll need to install the Cobra CLI globally. After installation, update your Go path to ensure all packages are properly available before using any Cobra commands in your application.
go install github.com/spf13/cobra-cli@latest
export PATH=$PATH:$(go env GOPATH)/bin
Intialize a Cobra Project
Initialize a cobra project with the following command
cobra-cli init
Create a new command using Cobra CLI
We can create a new command with cobra cli as follows
cobra-cli add products
This will create some boilerplate code for us inside the cmd folder inside products.go file. The boilerplate code looks something like this.
/*
Copyright © 2024 NAME HERE <EMAIL ADDRESS>
*/
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
// productsCmd represents the products command
var productsCmd = &cobra.Command{
Use: "products",
Short: "A brief description of your command",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("products called")
},
}
func init() {
rootCmd.AddCommand(productsCmd)
productsCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}
Create some variables and command flags to conditionally trigger the command
Next, let’s define some variables and set up command flags to control when and how our command is triggered. Command flags are a powerful way to allow users to modify the behavior of your CLI
var (
all bool
single int
)
func init() {
rootCmd.AddCommand(productsCmd)
productsCmd.Flags().BoolVarP(&all, "all", "a", false, "display all products")
productsCmd.Flags().IntVarP(&single, "single", "s", 0, "display just one product")
}
Create Products model
The below example is how you can create a products model using go structs. Products models will be needed to interact with the http call and save the data received from the Http endpoint.
// products/index.go
package products
import (
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"products_cli_app/utils"
"strconv"
"github.com/olekukonko/tablewriter"
)
type Product struct {
ID int `json:"id"`
Title string `json:"title"`
Price float64 `json:"price"`
Description string `json:"description"`
Category string `json:"category"`
Image string `json:"_"`
}
Create a function that fetches all products from the http request
Now, let's create a function that fetches all products via an HTTP request. This function will act as a bridge between our application and the server, retrieving product data for us to use. Whether you're building an e-commerce site or just need to display some product listings, this function will get the job done.
func FetchProducts() {
var p []Product
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"ID", "Title", "Price", "Description", "Category"})
url := "https://fakestoreapi.com/products"
response, err := http.Get(url)
if err != nil {
utils.Error("Cannot fetch products", err)
os.Exit(1)
}
body, err := io.ReadAll(response.Body)
if err != nil {
utils.Error("Cannot read body", err)
os.Exit(1)
}
defer response.Body.Close()
err = json.Unmarshal(body, &p)
if err != nil {
utils.Error("Cannot unmarshal", err)
os.Exit(1)
}
for _, v := range p {
table.Append([]string{
strconv.Itoa(v.ID),
v.Title,
fmt.Sprintf("%.2f", v.Price),
v.Description,
v.Category,
})
}
table.Render()
}
Create a function that fetches single product from the http endpoint
In this section, we’ll write a function to fetch a single product from an HTTP endpoint. This is perfect when you need detailed information about a specific item, like when users click on a product for more details. By retrieving just one product, we keep things efficient and focused.
func FetchSingleProduct(param int) {
var p Product
table := tablewriter.NewWriter(os.Stdout)
table.SetHeader([]string{"ID", "Title", "Price", "Description", "Category"})
url := fmt.Sprintf("%s/%v", "https://fakestoreapi.com/products", param)
response, err := http.Get(url)
if err != nil {
utils.Error("Cannot fetch products", err)
os.Exit(1)
}
body, err := io.ReadAll(response.Body)
if err != nil {
utils.Error("Cannot read body", err)
os.Exit(1)
}
defer response.Body.Close()
err = json.Unmarshal(body, &p)
if err != nil {
utils.Error("Cannot unmarshal", err)
os.Exit(1)
}
table.Append([]string{
strconv.Itoa(p.ID),
p.Title,
fmt.Sprintf("%.2f", p.Price),
p.Description,
p.Category,
})
table.Render()
}
Modify the Run method to conditional trigger the products
We have made available the product functions which fetch all products and single products. Now we can create a custom method inside the products.go file to conditionally call the functions if the user provides flags like —a or —s depending on the scenario
// cmd/products.go
var productsCmd = &cobra.Command{
Use: "products",
Short: "Fetches all products",
Run: executeCmd,
}
func executeCmd(cmd *cobra.Command, args []string) {
if all {
products.FetchProducts()
} else if single > 0 {
products.FetchSingleProduct(single)
}
}
Save all your commands
Now that we’ve set up our commands and flags, it’s time to save them all. This step is crucial as it ensures that the commands we’ve defined are stored and ready to use. Think of this as saving your work after setting everything up—you’ll want to make sure all your hard work is preserved so you can run your commands smoothly whenever you need them.
package main
import "products_cli_app/cmd"
func main() {
cmd.Execute()
}
Output
Execute the below command to get your output. This is how your output will look like. As we are using a tablewriter package. The output is formatted as a table.
Conclusion
In this article, we explored how to create a powerful command-line app using Go and the Cobra CLI framework. From setting up the project structure to adding commands and flags, you now have a solid foundation to build and customize your own CLI tools. Whether you are creating a utility for personal use or something that will help your team, Cobra makes it seamless and efficient.
If this guide helped you or sparked some new ideas, feel free to leave a comment below! Don’t forget to follow me on Hashnode for more articles like this. I'll be sharing more insights, tips, and tutorials on Go and other development topics. Let's keep building together!
Subscribe to my newsletter
Read articles from Mithilesh Tarkar directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by