Rester a CLI HTTP client using Resty, Survey, and Cobra.

I started this project because I'm probably obsessed with CLI tools and HTTP clients since the first time I was learning to develop an API and struggled to test the POST endpoint. For hours I was looking at how to do it and then I was overwhelmed by the options.

There are a lot of options, they are good and I used one of them. But I enjoy developing my own tools and writing about how to do it.

What I'm trying to achieve by developing Rester is an easy to use CLI tool to make HTTP requests. By the time I'm writing this article, this tool is far from finished, it just returns the JSON response from APIs. I don't know if sometime I will consider it finished. But I want to write about it.

I used Cobra, Survey, and Resty packages. It was the first time I used Resty, it was really easy to use, and its doc is really good.

Rester shows you five options: GET, POST, PUT, PATCH and DELETE.

image.png

To build it, we need to install the Cobra package.

go install github.com/spf13/cobra-cli@latest

After installing it, in the directory you will use to build the app, use the command line and run:

cobra-cli init

It will generate the main.go file and the cmd directory. Inside the cmd directory, is the root file.

Cobra-cli generates this code in root.go file:

var rootCmd = &cobra.Command{
  Use:   "demo",
  Short: "demo app to demonstrate cobra",
  Long: `demo app to demonstrate cobra by addition`
Run: func(cmd *cobra.Command, args []string) {}
}

func Execute() {
  if err := rootCmd.Execute(); err != nil {
    fmt.Println(err)
    os.Exit(1)
  }
}

We change it to make our program.

root.go

package cmd

import (
    "fmt"
    "log"
    "os"

    "github.com/AlecAivazis/survey/v2"
    "github.com/spf13/cobra"
)

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
    Use:   "rester_cli",
    Short: "A REST client cli",
    Long:  `A REST Client cli that support the methods GET, POST, DELETE and POST`,

    Run: func(cmd *cobra.Command, args []string) {
        httpMethod := ""
        prompt := &survey.Select{
            Message: "Choose an HTTP Method:",
            Options: []string{"GET", "POST", "PATCH", "PUT", "DELETE"},
        }
        survey.AskOne(prompt, &httpMethod)

        client := resty.New()

        switch httpMethod {
        case "GET":
            url := ""
            prompt := &survey.Input{
                Message: "Enter URL:",
            }
            survey.AskOne(prompt, &url)

            resp, err := client.R().EnableTrace().Get(url)

            if err != nil {
                log.Println(err)
            }
            fmt.Println(resp)

In the code above we import the Survey package to display the options: GET, POST, PUT, PATCH, DELETE. First, we write the variable httpMethod as an empty string, then we use the survey method Select and display the message "Choose an HTTP Method:" in the command line and display the options. The option selected will be stored in httpMethod.

Then we initialize a client to call the HTTP methods from Resty. After that, we use a switch statement to execute one of the HTTP methods in case it is selected.

For the case GET we write a variable named url as an empty string and use the Survey method Input, what you write in the command line will be stored in url and passed as an argument to Resty's function Get and the response is stored in resp and printed on the command line.

image.png

POST

case "POST":
            url := ""
            prompt := &survey.Input{
                Message: "Enter URL:",
            }
            survey.AskOne(prompt, &url)

            body := ""
            prompt = &survey.Input{
                Message: "Enter Body:",
            }
            survey.AskOne(prompt, &body)

            resp, err := client.R().SetBody(body).Post(url)

            if err != nil {
                log.Println(err)
            }

            fmt.Println(resp)

For the case POST, we write a variable named url as an empty string and use Survey's method Input. We do the same thing with the body of the request. url is passed as an argument to Resty's function Post, and body to SetBody. The response is stored in resp and printed on the command line.

image.png

PATCH

case "PATCH":
            url := ""
            prompt := &survey.Input{
                Message: "Enter URL:",
            }
            survey.AskOne(prompt, &url)

            body := ""
            prompt = &survey.Input{
                Message: "Enter Body:",
            }
            survey.AskOne(prompt, &body)

            resp, err := client.R().SetBody(body).Patch(url)

            if err != nil {
                log.Println(err)
            }

            fmt.Println(resp)

For the case PATCH, we write a variable named url as an empty string and use Survey's method Input. We do the same thing with the body of the request. url is passed as an argument to Resty's function Patch , and body to SetBody. The response is stored in resp and printed on the command line.

Yes, we did the same as POST, and it is the same with PUT.

PUT

case "PUT":
            url := ""
            prompt := &survey.Input{
                Message: "Enter URL:",
            }
            survey.AskOne(prompt, &url)

            body := ""
            prompt = &survey.Input{
                Message: "Enter Body:",
            }
            survey.AskOne(prompt, &body)

            resp, err := client.R().SetBody(body).Put(url)

            if err != nil {
                log.Println(err)
            }

            fmt.Println(resp)

image.png

DELETE

case "DELETE":
            url := ""
            prompt := &survey.Input{
                Message: "Enter URL:",
            }
            survey.AskOne(prompt, &url)

            resp, err := client.R().Delete(url)

            if err != nil {
                log.Println(err)
            }
            fmt.Println(resp)
        }

For the case DELETE, we write an url variable as an empty string and use Survey's method Input. url is passed as an argument to Resty's function Delete . The response is stored in resp and printed on the command line.

image.png

Conclusion

I will change the code to add more features like headers, and I'm not fully convinced about how to enter the data, I think to add more features I have to change that because possibly it will be more tedious to use the program. But this is the first iteration. If you have any suggestions about how to improve it or any features to add, leave a comment, open an issue in Github or contact me through Twitter, or LinkedIn.

Thanks for reading this article.

Source code here.

References: