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.
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. |