March 23, 2023

Sunghoon Kang

GRPC Gateway Client Generator

gRPC is a very popular alternative to REST for two reasons: high performance and code generation. High performance is achieved by using HTTP/2, which is a binary protocol that is more efficient than HTTP/1.1. Code generation is achieved by using Protocol Buffers that allow the generation of strongly typed client and data transfer objects for many languages.

If you use Golang, gRPC is arguably the best strongly typed API framework. The API performance improvement brought by HTTP/2 is also a nice enhancement. However, using the binary protocol and HTTP/2 is not always an option. It is challenging to consume gRPC API from a browser. If you are building an external API and want to lower the entry barrier for your users, then REST is still a safer choice.

Luckily there's a way to get the best of both worlds. You can build your API using gRPC and convert it to REST using grpc-gateway - an awesome open-source project that translates gRPC service definition to REST API. With grpc-gateway you can use gRPC to leverage code generation but still, expose a REST API to our users.

No problems with consuming our API from a browser anymore! However, if you need to consume your REST API from a Golang-based CLI tool, we can no longer use a generated client. Unfortunately, this is precisely the problem we've faced while building the Akuity Platform. The only solution we found is to generate an OpenAPI specification from gRPC and then use it to generate a Golang client.

What Is Missing in a Two-Layer Approach?

Unfortunately, this two-layer translation introduces unnecessary complexity and noise, significantly hurting generated client quality. We've tried multiple ways to generate OpenAPI specifications and many different Golang client generators. No luck.

We value developer productivity a lot so we decided to build our own solution. Please welcome the Akuity GRPC Gateway Client!

Akuity GRPC Gateway Client

The main idea is to eliminate the middleman and generate high-quality Golang clients directly from the gRPC service definition. This approach allows us to reduce the number of tools involved in the generation and make sure that generated client is almost identical to the original gRPC definition.

The best way to describe the tool is to show it in action!

Try It In Action

The source code of the usage example is available at grpc-gateway-client/example. This example contains a simple gRPC service and a CLI tool that consumes it. The service is a classic "Hello World" service that accepts a name and returns a greeting:

syntax = "proto3";

package helloworld;

import "google/api/annotations.proto";

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {
    option (google.api.http) = {
      get: "/v1/example/echo"
    };
  }
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

There are several ways to generate a gRPC service from the definition. We are big fans of buf, so we will use it in our example. In order to use the client generator, we need to install the generator binary using go install github.com/akuity/grpc-gateway-client/protoc-gen-grpc-gateway-client@latest and configure it in buf.generate.yaml:

version: v1
managed:
  enabled: true
  go_package_prefix:
    default: github.com/akuity/grpc-gateway-client/internal/test/gen
    except:
      - buf.build/googleapis/googleapis
      - buf.build/grpc-ecosystem/grpc-gateway
plugins:
  - name: grpc-gateway-client
    path: bin/protoc-gen-grpc-gateway-client
    out: internal/test/gen
    opt:
      - paths=source_relative

Now we can generate the client using buf generate and use it in our CLI tool:

client := grpc_gateway_client_example.NewGreeterGatewayClient(gateway.NewClient(baseURL))
resp, err := client.SayHello(context.Background(), &grpc_gateway_client_example.HelloRequest{Name: "World"})
if err != nil {
    panic(err)
}
println(resp.Message)

Conclusion

We hope that you will find this tool useful! If you have any questions or suggestions, please file an issue inside the grpc-gateway-client Github repository. Don't forget to ⭐ the repository if you like it!

Get Started

Try the Akuity Platform free for 30 days.
No credit card required.

Man and woman throwing ball between themselves

Get Started

Try the Akuity Platform free for 30 days.
No credit card required.

Man and woman throwing ball between themselves

Get Started

Try the Akuity Platform free for 30 days.
No credit card required.

Man and woman throwing ball between themselves