gRPC transcoding

gRPC transcoding allows your services to accept both gRPC and HTTP/JSON (accepting/replying the application/json content type) requests, providing greater flexibility. This feature is particularly useful when:

  • You want to expose your gRPC services to clients that don’t support gRPC

  • You need to support both traditional REST APIs and gRPC endpoints

  • You want to leverage gRPC’s efficiency while maintaining HTTP/JSON compatibility

You do not need specific configuration for gRPC transcoding since it is serves HTTP protocol.

Services are transcoded out of the box, however you will get the best of transcoding with additional configuration such as the mount path to have transcoding operational.

transcoding is in tech preview in Vert.x 5.0 until the API becomes stable.

Using transcoding

To use Vert.x gRPC transcoding, add the following dependency to the dependencies section of your build descriptor:

  • Maven (in your pom.xml):

<dependency>
  <groupId>io.vertx</groupId>
  <artifactId>vertx-grpc-transcoding</artifactId>
  <version>5.0.1-SNAPSHOT</version>
</dependency>
  • Gradle (in your build.gradle file):

dependencies {
  compile 'io.vertx:vertx-grpc-transcoding:5.0.1-SNAPSHOT'
}

Idiomatic transcoding generation

The Vert.x gRPC protoc plugin generates transcoding definitions for the gRPC services.

The plugin supports generating transcoding definitions for the gRPC services via http.proto.

The google.api.http annotation allows you to map gRPC methods to HTTP endpoints, enabling your service to handle both gRPC and HTTP/REST requests. The plugin supports various HTTP methods (GET, POST) and custom methods.

Example of the gRPC transcoding definition
syntax = "proto3";

import "google/api/http.proto";

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply) {
    option (google.api.http) = {
      post: "/v1/hello"
      additional_bindings {
        get: "/v1/hello/{name}"
      }
    };
  }
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

Transcoding service methods are generated by the Vert.x gRPC protoc plugin, when the service declares an HttpRule:

Service service = new GreeterGrpcService() {
  @Override
  public Future<HelloReply> sayHello(HelloRequest request) {
    return Future.succeededFuture(HelloReply.newBuilder().setMessage("Hello " + request.getName()).build());
  }
};

server.addService(service);
the service responds to all the server enabled protocols, by default that is application/grpc, application/grpc+json, application/grpc-web, application/grpc-web-text and of course application/json

To test gRPC transcoding, you can use a tool like curl to send HTTP requests to your gRPC service.

curl -X POST -H "Content-Type: application/json" -d '{"name":"vert.x"}' http://localhost:8080/v1/hello

And you can also request the path mapped version:

curl -X GET -H "Content-Type: application/json" http://localhost:8080/v1/hello/vert.x

Automatic HTTP mapping

When a gRPC service is deployed without specific transcoding configuration, the service is mapped to HTTP POST, request/response body will be converted to the service message by the transcoder.

Basic HTTP mappings

service Greeter {
  // Maps a GET endpoint with a URL parameter
  rpc SayHello (HelloRequest) returns (HelloReply) {
    option (google.api.http) = {
      get: "/v1/hello/{name}"
      additional_bindings {
        post: "/v1/hello"  // Alternative POST endpoint
      }
    };
  }

  // Maps a POST endpoint with an alternative GET binding
  rpc SayHelloAgain (HelloRequest) returns (HelloReply) {
    option (google.api.http) = {
      post: "/v2/hello"
      additional_bindings {
        get: "/v2/hello/{name}"
      }
    };
  }
}

Advanced Configurations

Custom Methods

service Greeter {
  // Define custom HTTP methods
  rpc SayHelloCustom (HelloRequest) returns (HelloReply) {
    option (google.api.http) = {
      custom: {
        kind: "ACL"
        path: "/v1/hello/custom/{name}"
      }
    };
  }
}

Request Body Handling

service Greeter {
  // Specify which field should be mapped to the HTTP request body
  rpc SayHelloWithBody (HelloBodyRequest) returns (HelloReply) {
    option (google.api.http) = {
      post: "/v1/hello/body"
      body: "request"  // Maps the "request" field to the request body
    };
  }
}

message HelloBodyRequest {
  HelloRequest request = 1;
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string reply = 1;
}

Response Body Mapping

service Greeter {
  // Configure which field should be used as the HTTP response body
  rpc SayHelloWithResponseBody (HelloRequest) returns (HelloBodyResponse) {
    option (google.api.http) = {
      post: "/v1/hello/body/response"
      response_body: "response"  // Maps the "response" field to the response body
    };
  }
}

message HelloRequest {
  string name = 1;
}

message HelloBodyResponse {
  HelloResponse response = 1;
}

message HelloResponse {
  string reply = 1;
}

Transcoding error handling

If an error occurs during transcoding, the server will return an HTTP error response with the appropriate status code. Most grpc status codes are mapped to the corresponding HTTP status codes on best effort basis. If the status code is not mapped, the server will return a 500 Internal Server Error.

gRPC Status Code

HTTP Status Code

Description

OK

200

The operation completed successfully.

CANCELLED

408

The operation was cancelled (typically by the caller).

UNKNOWN

500

Unknown error.

INVALID_ARGUMENT

400

Client specified an invalid argument.

DEADLINE_EXCEEDED

504

Deadline expired before operation could complete.

NOT_FOUND

404

Some requested entity (e.g., file or directory) was not found.

ALREADY_EXISTS

409

Some entity that we attempted to create (e.g., file or directory) already exists.

PERMISSION_DENIED

403

The caller does not have permission to execute the specified operation.

RESOURCE_EXHAUSTED

429

Some resource has been exhausted, perhaps a per-user quota, or perhaps the entire file system is out of space.

FAILED_PRECONDITION

400

Operation was rejected because the system is not in a state required for the operation’s execution

ABORTED

409

The operation was aborted, typically due to a concurrency issue like sequencer check failures, transaction aborts, etc.

OUT_OF_RANGE

400

Operation was attempted past the valid range.

UNIMPLEMENTED

501

Operation is not implemented or not supported/enabled in this service.

INTERNAL

500

Internal errors. This means that some invariants expected by the underlying system have been broken.

UNAVAILABLE

503

The service is currently unavailable.

DATA_LOSS

500

Unrecoverable data loss or corruption.

UNAUTHENTICATED

401

The request does not have valid authentication credentials for the operation.