elixir-grpc / grpc

An Elixir implementation of gRPC
https://hex.pm/packages/grpc
Apache License 2.0
1.38k stars 212 forks source link

Add gRPC http/json transcoding support #274

Closed drowzy closed 5 months ago

drowzy commented 1 year ago

gRPC Transcoding is a feature for mapping between a gRPC method and one or more HTTP REST endpoints. It allows developers to build a single API service that supports both gRPC APIs and REST APIs.

From a user perspective it involves adding gRPC-gateway annotations to your proto to define how JSON requests and responses should be mapped to the gRPC service. The benefit is that you can offer a REST api in addition to gRPC, but you're only required to implement the handler once.

For instance the helloworld.proto would look something like this:

import "google/api/annotations.proto";
import "google/protobuf/timestamp.proto";

package helloworld;

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

  rpc SayHelloFrom (HelloRequestFrom) returns (HelloReply) {
    option (google.api.http) = {
      post: "/v1/greeter"
      body: "*"
    };
  }
}

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

// HelloRequestFrom!
message HelloRequestFrom {
  // Name!
  string name = 1;
  // From!
  string from = 2;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
  google.protobuf.Timestamp today = 2;
}

There's a draft PR to check out: #273 Related issue: #192

Caveats / shortcomings

Two possible solutions:

  1. Include a protoc plugin in this library which supersedes the grpc plugin option in protobuf generating the protobuf message definitions and grpc service definition would require a call sort of like this:
protoc --elixir_out=:./lib/ --grpc_elixir_out=./lib helloworld.proto

Which would create two files in lib. One with the messages (helloworld.pb.ex) and one with the service definitions helloworld.pb.svc.ex). This is the approach used in twirp for instance.

  1. Provide a seperate protoc plugin in a different repository (like grpc-gateway) which would include the Google.Api extensions instead.
sleipnir commented 1 year ago

@drowzy I prefer the first approach to generate service.pb.svc.ex files.

polvalente commented 1 year ago

Extensions cannot be provided at runtime (at least to my knowledge) only those defined in elixir-protobuf already.

This seems like an upstream issue that could be tackled in :protobuf. Perhaps they could provide a config (either application config or a config file like Credo uses) for that?

drowzy commented 1 year ago

This seems like an upstream issue that could be tackled in :protobuf. Perhaps they could provide a config (either application config or a config file like Credo uses) for that?

Hmm, yes the issue more or less resolves itself if the code gen was called per project instead through a global script via protoc. I'll create an issue in protobuf and we'll see :)