Envoy’s protocol transcoding lets you expose gRPC services as RESTful HTTP/JSON APIs without touching your gRPC service code.

Let’s see it in action. Imagine a simple gRPC service that echoes a message.

// echo.proto
syntax = "proto3";

package echo;

service EchoService {
  rpc Echo(EchoRequest) returns (EchoResponse);
}

message EchoRequest {
  string message = 1;
}

message EchoResponse {
  string message = 1;
}

Here’s how you’d configure Envoy to expose this gRPC service as a REST endpoint:

# envoy.yaml
admin:
  access_log_path: /tmp/envoy.log
  address:
    socket_address:
      address: 0.0.0.0
      port_value: 9901
static_resources:
  listeners:
  - name: listener_0
    address:
      socket_address:
        address: 0.0.0.0
        port_value: 8080
    filter_chains:
    - filters:
      - name: envoy.filters.network.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
          stat_prefix: ingress_http
          codec_type: AUTO
          route_config:
            name: local_route
            virtual_hosts:
            - name: local_service
              domains: ["*"]
              routes:
              - match:
                  prefix: "/echo"
                route:
                  cluster: echo_grpc_cluster
                  # This is the magic for transcoding
                  upgrade_configs:
                  - enabled: true
                    # This tells Envoy to use the protocol transcoding filter
                    filters:
                    - name: envoy.filters.http.proto_message
                      typed_config:
                        "@type": type.googleapis.com/envoy.extensions.filters.http.proto_message.v3.ProtoMessageConfig
                        # Use proto_descriptor to specify the proto definition
                        proto_descriptor_bin: !!binary |
                          # Base64 encoded content of your compiled proto descriptor set
                          # Example: protoc --descriptor_set_out=echo.pb echo.proto
                          # Then base64 encode echo.pb
                          <YOUR_BASE64_ENCODED_PROTO_DESCRIPTOR_SET>
                        # Specify the service and method to be invoked
                        service_name: "echo.EchoService"
                        method_name: "Echo"
          http_filters:
          - name: envoy.filters.http.router
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
  clusters:
  - name: echo_grpc_cluster
    connect_timeout: 0.25s
    type: STRICT_DNS
    lb_policy: ROUND_ROBIN
    # This is where your actual gRPC service runs
    # Assuming it's on localhost:50051
    load_assignment:
      cluster_name: echo_grpc_cluster
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: 127.0.0.1
                port_value: 50051
    # Indicate that this cluster serves gRPC traffic
    typed_extension_protocol_options:
      envoy.extensions.grpc.v3.GrpcProtocolOptions:
        "@type": type.googleapis.com/envoy.extensions.grpc.v3.GrpcProtocolOptions
        grpc_http1_request_metadata_headers: # Optional: for gRPC-web
        - name: "x-grpc-web"
          value: "1"
        - name: "x-user-agent"
          value: "grpc-envoy-javascript/0.0.0"

To make this work, you need to compile your .proto file into a descriptor set (.pb file) and then base64 encode its content.

# Compile proto to descriptor set
protoc --descriptor_set_out=echo.pb echo.proto

# Base64 encode the descriptor set
base64 echo.pb > echo.pb.b64

Then, paste the content of echo.pb.b64 into the proto_descriptor_bin field in envoy.yaml.

Now, if you have a gRPC server running on localhost:50051 that implements EchoService.Echo, you can send a POST request to http://localhost:8080/echo with a JSON body like:

{
  "message": "hello from rest"
}

Envoy will:

  1. Receive the HTTP/JSON request.
  2. Use the ProtoMessage filter and the provided descriptor set to parse the JSON into a gRPC EchoRequest protobuf message.
  3. Route the request to the echo_grpc_cluster.
  4. Convert the EchoRequest protobuf message into a gRPC request format (HTTP/2 with content-type: application/grpc).
  5. Send it to your gRPC server.
  6. Receive the EchoResponse protobuf message from the gRPC server.
  7. Convert the EchoResponse protobuf message back into an HTTP/JSON response.
  8. Send the JSON response back to the client.

The system solves the problem of needing two separate APIs for the same backend service, or forcing clients to use gRPC when they prefer REST. Envoy acts as a seamless translation layer.

The core mechanism for this translation is the envoy.filters.http.proto_message filter, which leverages a proto_descriptor_bin to understand the structure of your Protobuf messages. This descriptor set acts as a mini-compiler within Envoy, allowing it to dynamically serialize and deserialize messages based on the schema.

A crucial, often overlooked detail is how Envoy maps HTTP request methods and paths to gRPC services and methods. By default, the proto_message filter expects a specific mapping. For a POST request to /echo, it assumes you want to call the Echo method of the EchoService. If your gRPC service has different naming conventions or you want to expose an RPC like GetUserInfo via a GET request to /users/{id}, you’ll need to configure Envoy’s router and potentially use multiple proto_message filters or more advanced routing rules. The route configuration in the envoy.yaml is where you’d specify which Envoy route matches which gRPC method, and the proto_message filter then handles the message serialization/deserialization for that specific method.

The next concept you’ll likely encounter is handling more complex gRPC features like streaming or bidirectionality, which require more intricate Envoy configurations and might not be fully supported by simple REST transcoding.

Want structured learning?

Take the full Envoy course →