Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Any way to create a CLI out of the web server? #585

Open
ashishb opened this issue Sep 20, 2024 · 5 comments
Open

Any way to create a CLI out of the web server? #585

ashishb opened this issue Sep 20, 2024 · 5 comments
Labels
question Further information is requested

Comments

@ashishb
Copy link

ashishb commented Sep 20, 2024

Thanks for this awesome package.
Is there a way to convert my webserver into an offline CLI as well using huma?

@ashishb ashishb changed the title Any way to create a CLI out of the web server Any way to create a CLI out of the web server? Sep 21, 2024
@danielgtaylor danielgtaylor added the question Further information is requested label Oct 8, 2024
@danielgtaylor
Copy link
Owner

@ashishb thanks! I'm glad you like it 😄 What are you hoping to do with your CLI? If you use the humacli package you can easily add extra commands, for example the tutorial shows how you can create a command to dump the OpenAPI document without running a server:

If you are looking to have a CLI that calls into a running web server, take a look at Restish

@ashishb
Copy link
Author

ashishb commented Oct 14, 2024

@danielgtaylor I have a set of API calls generated via huma and I want to automatically convert it into CLI

@danielgtaylor
Copy link
Owner

@ashishb did you read the linked docs? I think they will show you how to do what you want. Let me know if you have specific questions!

@ashishb
Copy link
Author

ashishb commented Oct 17, 2024

@danielgtaylor I did look at https://huma.rocks/tutorial/cli-client/
I don't need a client.
I want to convert the server to the CLI as-is, so, I can run the server as a CLI tool.

@danielgtaylor
Copy link
Owner

@ashishb you can use the custom commands docs I linked to if you want CLI commands for the server itself. Here's a modified hello world example:

package main

import (
	"context"
	"fmt"
	"net/http"
	"net/http/httptest"

	"github.com/danielgtaylor/huma/v2"
	"github.com/danielgtaylor/huma/v2/adapters/humachi"
	"github.com/danielgtaylor/huma/v2/humacli"
	"github.com/danielgtaylor/huma/v2/humatest"
	"github.com/go-chi/chi/v5"
	"github.com/spf13/cobra"

	_ "github.com/danielgtaylor/huma/v2/formats/cbor"
)

// Options for the CLI.
type Options struct {
	Port int `help:"Port to listen on" short:"p" default:"8888"`
}

// GreetingInput represents the greeting operation request.
type GreetingInput struct {
	Name string `path:"name" maxLength:"30" example:"world" doc:"Name to greet"`
}

// GreetingOutput represents the greeting operation response.
type GreetingOutput struct {
	Body struct {
		Message string `json:"message" example:"Hello, world!" doc:"Greeting message"`
	}
}

func main() {
	var router *chi.Mux

	// Create a CLI app which takes a port option.
	cli := humacli.New(func(hooks humacli.Hooks, options *Options) {
		// Create a new router & API
		router = chi.NewMux()
		api := humachi.New(router, huma.DefaultConfig("My API", "1.0.0"))

		// Register GET /greeting/{name}
		huma.Register(api, huma.Operation{
			OperationID: "get-greeting",
			Summary:     "Get a greeting",
			Method:      http.MethodGet,
			Path:        "/greeting/{name}",
		}, func(ctx context.Context, input *GreetingInput) (*GreetingOutput, error) {
			resp := &GreetingOutput{}
			resp.Body.Message = fmt.Sprintf("Hello, %s!", input.Name)
			return resp, nil
		})

		// Tell the CLI how to start your router.
		hooks.OnStart(func() {
			http.ListenAndServe(fmt.Sprintf(":%d", options.Port), router)
		})
	})

	cli.Root().AddCommand(&cobra.Command{
		Use:   "get-greeting",
		Short: "Greet someone",
		Args:  cobra.ExactArgs(1),
		Run: func(cmd *cobra.Command, args []string) {
			req := httptest.NewRequest(http.MethodGet, "/greeting/"+args[0], nil)
			resp := httptest.NewRecorder()
			router.ServeHTTP(resp, req)
			humatest.PrintResponse(resp.Result())
		},
	})

	// Run the CLI. When passed no commands, it starts the server.
	cli.Run()
}

You can run it like:

$ go run . get-greeting bob
HTTP/1.1 200 OK
Connection: close
Content-Type: application/json
Link: </schemas/GreetingOutputBody.json>; rel="describedBy"

{
  "$schema": "https://example.com/schemas/GreetingOutputBody.json",
  "message": "Hello, bob!"
}

You can write whatever commands you want this way and simulate a request to the server. If you don't want the overhead of HTTP request/response stuff, you can instantiate your handler's request struct manually and call it directly, then handle the response struct however you want, or factor out common code that both the CLI operation and handler can call. Hopefully this helps!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants