[[grpc-transcoding]]
== 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.

NOTE: 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`):

[source,xml,subs="+attributes"]
----
<dependency>
  <groupId>io.vertx</groupId>
  <artifactId>vertx-grpc-transcoding</artifactId>
  <version>5.0.7</version>
</dependency>
----

* Gradle (in your `build.gradle` file):

[source,groovy,subs="+attributes"]
----
dependencies {
  compile 'io.vertx:vertx-grpc-transcoding:5.0.7'
}
----

=== Idiomatic transcoding generation

The <<vertx-grpc-protoc-plugin,Vert.x gRPC protoc plugin>> generates transcoding definitions for the gRPC services.

The plugin supports generating transcoding definitions for the gRPC services via https://github.com/googleapis/api-common-protos/blob/main/google/api/http.proto[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
[source,proto]
----
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`:

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

server.addService(service);
----

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

[source]
----
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:

[source]
----
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

[source,proto]
----
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
[source,proto]
----
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
[source,proto]
----
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
[source,proto]
----
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.
|===