Skip to content

go-woo/protoc-gen-echo

Repository files navigation

What is protoc-gen-echo?

protoc-gen-echo is a protoc plug-in that generates Echo server code from proto file.

If you want to create a Echo's http api /helloworld/:name/hi/:nice, you just need to add rpc in a proto file, and generate it.

/helloworld/:name/hi/:nice mapping http api will be generated by protoc-gen-echo.

protoc-gen-echo can generate complete backend all code combination with protoc-gen-ent.

** About ent you can find more help from ent.

Quick start

Step 0: Pre-installation on ubuntu

sudo apt install protobuf-compiler make

Step 1: Edit ./example/v1/greeter.proto

service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {
    option (google.api.http) = {
      get: "/helloworld/{name}/hi/{nice}"
    };
  }
  //...
}
//...

Step 2: Generate

You can generate code used by make:

make example

Or you can generate code used by below too:

go install google.golang.org/protobuf/cmd/protoc-gen-go@latest

go install github.com/go-woo/protoc-gen-echo@latest

protoc --proto_path=. \
    --proto_path=./third_party \
    --go_out=paths=source_relative:. \
    --echo_out=paths=source_relative:. \
    ./example/v1/greeter.proto

The protoc-gen-echo generated 2 files:greeter_router.pb.go and greeter_handler.pb.go.

The greeter.pb.go was generated by protoc-gen-go

More help can be found in protoc.

Your business logic stubs has been generated in your_xxxx_handler.pb.go, You can edit business logic in stubs.

func $(YourService)$(RpcName)BusinessHandler(payload *YourRequest) (YourReply, error) {
	// Here can put your business logic, can use ORM:github.com/go-woo/protoc-gen-ent
	return YourReply{}, nil
}

All handlers typo can be found in your_xxxx_router.pb.go.

Step 3: Write example business logic

For this example, in greeter_router.pb.go. You can find generated echo's handler _Greeter_SayHello0_HTTP_Handler.

func RegisterGreeterRouter(e *echo.Echo) {
	e.GET("/helloworld/:name/hi/:nice", _Greeter_SayHello0_HTTP_Handler)
	//...
}

In same file, you can find _Greeter_SayHello0_HTTP_Handler's implement:

func _Greeter_SayHello0_HTTP_Handler(c echo.Context) error {
	var req *HelloRequest = new(HelloRequest)

	req.Name = c.Param(strings.ToLower("Name"))
	req.Nice = c.Param(strings.ToLower("Nice"))
	reply, err := GreeterSayHelloBusinessHandler(req, c)
	if err != nil {
		return err
	}

	return c.JSON(http.StatusOK, &reply)
}

Our focus is on GreeterSayHelloBusinessHandler(payload). In greeter_handler.pb.go, you can write business logic.

func GreeterSayHelloBusinessHandler(req *HelloRequest, c echo.Context) (HelloReply, error) {
	// Here can put your business logic,protoc-gen-ent soon coming
	reqJson, err := json.Marshal(req)
	if err != nil {
		return HelloReply{}, err
	}
	fmt.Printf("Got HelloRequest is: %v\n", string(reqJson))

	return HelloReply{}, nil
}

For running this example we need to write a main.go

package main

import (
	v1 "github.com/go-woo/protoc-gen-echo/example/v1"
	"github.com/labstack/echo/v4"
	"github.com/labstack/echo/v4/middleware"
)

func main() {
	e := echo.New()

	e.Use(middleware.Logger())
	e.Use(middleware.Recover())

	v1.RegisterGreeterRouter(e)
	// you can add custom router outside protoc-gen-echo too.
	// MyCustomRouter(e)

	e.Logger.Fatal(e.Start(":1323"))
}

More help and doc can be found on Echo , include rate limited, auto update TLS cert, etc.

Step 4: Run example

cd example && go run main.go

Open browser, URL:http://localhost:1223/helloworld/Lok-Tar/hi/Ogar

Or shell execute

curl -X GET http://localhost:1323/helloworld/Lok-Tar/hi/Ogar

Step 5: Optional JWT support

Service option

//get token URL
option (google.api.default_host) = "/login";
//need auth root path, can multi path
option (google.api.oauth_scopes) =
"/restricted,"

was used for JWT describer.

If you want to support JWT, can add it. Else you can comment it.

‼️ Special notes

  • Do not remove your_xxxx_handler.pb.go. It was generated only when the first time, and will not be generated or overwritten after that again, because the business logic code you added is already in it.

  • JSON format in http body. If http client request has body, header should has Content-Type: application/json

  • Validate. Single source(proto) is the most important means to ensure consistency. protobuf message filed validate can use protoc-gen-validate.

  • protoc-gen-echo follow google.api.httprule.

  • ent still does not support message nesting, so proto http rule body must be *.

Generate gRPC

gRPC generated by protoc-gen-go-grpc

go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

Add --go-grpc_out=paths=source_relative:. \

protoc --proto_path=. \
    --proto_path=./third_party \
    --go_out=paths=source_relative:. \
    --echo_out=paths=source_relative:. \
    --go-grpc_out=paths=source_relative:. \
    ./example/v1/greeter.proto

Generate Open Api Spec document

OAS generated by protoc-gen-openapi

go install github.com/google/gnostic/cmd/protoc-gen-openapi@latest

Add --openapi_out=paths=source_relative:. \

protoc --proto_path=. \
    --proto_path=./third_party \
    --go_out=paths=source_relative:. \
    --echo_out=paths=source_relative:. \
    --openapi_out=paths=source_relative:. \
    ./example/v1/greeter.proto

Generated yaml file can be browser on swagger or openapi

Validate

Fields validate generated by protoc-gen-validate

go install github.com/envoyproxy/protoc-gen-validate@latest

Add --validate_out="lang=go:." \

protoc --proto_path=. \
    --proto_path=./third_party \
    --go_out=paths=source_relative:. \
    --echo_out=paths=source_relative:. \
    --validate_out="lang=go:." \
    ./example/v1/greeter.proto

Todo

  • Add generate jwt openapi/swagger support
https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication
https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#securitySchemeObject
https://github.com/grpc-ecosystem/grpc-gateway/blob/master/examples/internal/proto/examplepb/a_bit_of_everything.proto