Skip to content
You are viewing the latest documentation for Solo Enterprise for kgateway. To access the documentation for older versions, use the version switcher.

For the complete documentation index, see llms.txt. Markdown versions of all docs pages are available by appending .md to any docs URL.

gRPC-JSON transcoding

Page as Markdown

Configure HTTP-to-gRPC transcoding so that REST clients can communicate with gRPC backends through the gateway.

About gRPC-JSON transcoding

With gRPC-JSON transcoding, the gateway accepts incoming HTTP/JSON requests and transparently converts them into gRPC calls for your backend services. The gRPC response is also transcoded back to JSON and returned to the HTTP client. This way, REST clients, such as browsers and legacy systems, can talk to gRPC services without any protocol-specific code.

    sequenceDiagram
    participant Client as REST client
    participant Gateway
    participant Backend as gRPC service

    Client->>Gateway: HTTP POST /yages.Echo/Ping
    Gateway->>Backend: gRPC yages.Echo/Ping
    Backend-->>Gateway: gRPC response
    Gateway-->>Client: HTTP 200 {"text":"pong"}
  

You can map HTTP paths to gRPC methods in the following ways.

  • Auto-mapping: Enable autoMapping to automatically route POST /package.Service/Method requests to the corresponding gRPC method. No changes to the .proto file are required. This is the simpler option and works well for third-party protos that you do not control.
  • HTTP annotations: Add google.api.http annotations to your .proto file to define explicit REST-to-gRPC mappings. For example, map GET /v1/shelves to the ListShelves gRPC method. Use this when you need custom URL paths, HTTP methods other than POST, or field-level mappings to path segments or query parameters.

gRPC-JSON transcoding is configured by setting the entGrpcJsonTranscoder field in an EnterpriseKgatewayTrafficPolicy resource and attaching it to an HTTPRoute. The policy activates Envoy’s native grpc_json_transcoder filter only for the targeted routes, so other routes on the same gateway are unaffected.

Note

Your gRPC backend must be configured for HTTP/2. Set appProtocol: http2 on the Service port to enable HTTP/2 connections to the backend. Without HTTP/2, transcoding fails at runtime. For more information, see HTTP/2.

Before you begin

  1. Follow the Get started guide to install Solo Enterprise for kgateway.

  2. Follow the Sample app guide to create a gateway proxy with an HTTP listener and deploy the httpbin sample app.

  3. Get the external address of the gateway and save it in an environment variable.

    export INGRESS_GW_ADDRESS=$(kubectl get svc -n kgateway-system http -o jsonpath="{.status.loadBalancer.ingress[0]['hostname','ip']}")
    echo $INGRESS_GW_ADDRESS  
    kubectl port-forward deployment/http -n kgateway-system 8080:8080

  1. Install grpcurl to test gRPC services directly.
  2. Install curl to test transcoded HTTP requests.
  3. Optional: Install protoc if you want to compile a proto descriptor from your own .proto files.

Deploy a sample app

Deploy the yages gRPC echo service. The guide demonstrates HTTP/JSON to gRPC transcoding by using the yages.Echo.Ping method, which takes no input and returns {"text":"pong"}. The service also exposes yages.Echo.Reverse, which you can test directly with grpcurl.

  1. Deploy the gRPC echo server.

    kubectl apply -f - <<EOF
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: grpc-echo
      namespace: kgateway-system
      labels:
        app: grpc-echo
    spec:
      selector:
        matchLabels:
          app: grpc-echo
      replicas: 1
      template:
        metadata:
          labels:
            app: grpc-echo
        spec:
          containers:
            - name: grpc-echo
              image: ghcr.io/projectcontour/yages:v0.1.0
              ports:
                - containerPort: 9000
                  protocol: TCP
              env:
                - name: POD_NAME
                  valueFrom:
                    fieldRef:
                      fieldPath: metadata.name
                - name: NAMESPACE
                  valueFrom:
                    fieldRef:
                      fieldPath: metadata.namespace
                - name: GRPC_ECHO_SERVER
                  value: "true"
                - name: SERVICE_NAME
                  value: grpc-echo
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: grpc-echo-svc
      namespace: kgateway-system
      labels:
        app: grpc-echo
    spec:
      type: ClusterIP
      ports:
        - port: 3000
          protocol: TCP
          targetPort: 9000
          appProtocol: http2
      selector:
        app: grpc-echo
    EOF

    The appProtocol: http2 tells the gateway to use HTTP/2 for connections to this service, which is required for gRPC-JSON transcoding.

  2. Verify that the pod is running.

    kubectl get pods -n kgateway-system -l app=grpc-echo

    Example output:

    NAME                         READY   STATUS    RESTARTS   AGE
    grpc-echo-5fc549b5fc-tdlzw   1/1     Running   0          30s
    

Configure gRPC-JSON transcoding

  1. Create an HTTPRoute that forwards traffic along the grpc.example.com domain to the gRPC echo service.

    kubectl apply -f - <<EOF
    apiVersion: gateway.networking.k8s.io/v1
    kind: HTTPRoute
    metadata:
      name: grpc-transcoding-route
      namespace: kgateway-system
      labels:
        app: grpc-echo
    spec:
      parentRefs:
      - name: http
        namespace: kgateway-system
      hostnames:
      - "grpc.example.com"
      rules:
      - matches:
        - path:
            type: PathPrefix
            value: /
        backendRefs:
        - name: grpc-echo-svc
          port: 3000
    EOF
  2. Prepare the proto descriptor. The gRPC-JSON transcoder requires a compiled proto descriptor to know how to map HTTP requests to gRPC calls. You provide this mapping as base64-encoded bytes in the EnterpriseKgatewayTrafficPolicy in the next step.

    For the yages echo service, you can use the pre-compiled descriptor directly. Save it to an environment variable:

    PROTO_DESCRIPTOR="CqEBCgt5YWdlcy5wcm90bxIFeWFnZXMiBwoFRW1wdHkiHQoHQ29udGVudBISCgR0ZXh0GAEgASgJUgR0ZXh0MlsKBEVjaG8SJgoEUGluZxIMLnlhZ2VzLkVtcHR5Gg4ueWFnZXMuQ29udGVudCIAEisKB1JldmVyc2USDi55YWdlcy5Db250ZW50Gg4ueWFnZXMuQ29udGVudCIAYgZwcm90bzM="

    If you have a .proto file for your service, compile it into a descriptor with protoc. Use --include_imports to make the descriptor self-contained.

    protoc \
      --include_imports \
      --descriptor_set_out=service.pb \
      path/to/your/service.proto
    
    PROTO_DESCRIPTOR=$(base64 -i service.pb)

    For large descriptors or when you want to update the descriptor without changing the EnterpriseKgatewayTrafficPolicy, store it in a ConfigMap. See Use a ConfigMap for the proto descriptor for instructions, then reference it in the EnterpriseKgatewayTrafficPolicy resource by using the protoDescriptorConfigMap field.

  3. Create an EnterpriseKgatewayTrafficPolicy resource to configure the transcoder with your HTTP/JSON to gRPC mapping.

    kubectl apply -f - <<EOF
    apiVersion: enterprisekgateway.solo.io/v1alpha1
    kind: EnterpriseKgatewayTrafficPolicy
    metadata:
      name: grpc-transcoding-policy
      namespace: kgateway-system
      labels:
        app: grpc-echo
    spec:
      targetRefs:
      - group: gateway.networking.k8s.io
        kind: HTTPRoute
        name: grpc-transcoding-route
      entGrpcJsonTranscoder:
        protoDescriptorBin: ${PROTO_DESCRIPTOR}
        services:
        - yages.Echo
        autoMapping: true
        convertGrpcStatus: true
    EOF
    Review the following table to understand this configuration.
    SettingDescription
    entGrpcJsonTranscoder.protoDescriptorBinThe compiled proto descriptor as a base64-encoded string.
    entGrpcJsonTranscoder.servicesThe fully-qualified gRPC service names to transcode (for example, yages.Echo or main.Bookstore). Every name must exist in the descriptor.
    entGrpcJsonTranscoder.autoMappingWhen true, automatically routes POST /package.Service/Method requests to the corresponding gRPC method without requiring google.api.http annotations in the proto.
    entGrpcJsonTranscoder.convertGrpcStatusWhen true, converts gRPC status trailers into HTTP status codes and JSON error bodies, so REST clients receive standard HTTP error semantics.

Verify transcoding

Verify that the gateway proxy transcodes HTTP requests to gRPC.

  1. Send an HTTP POST request to the Ping method. With autoMapping enabled, the gateway maps the path POST /yages.Echo/Ping to the gRPC Ping method.

    curl -X POST \
      -H "Host: grpc.example.com" \
      -H "Content-Type: application/json" \
      -d '{}' \
      http://$INGRESS_GW_ADDRESS:8080/yages.Echo/Ping
    curl -X POST \
      -H "Host: grpc.example.com" \
      -H "Content-Type: application/json" \
      -d '{}' \
      http://localhost:8080/yages.Echo/Ping

    Example output:

    {"text":"pong"}
    
  2. Optional: Confirm that native gRPC clients can still reach the service directly.

    grpcurl -plaintext \
      -authority grpc.example.com \
      $INGRESS_GW_ADDRESS:8080 \
      yages.Echo/Ping
    grpcurl -plaintext \
      -authority grpc.example.com \
      localhost:8080 \
      yages.Echo/Ping

    Example output:

    {"text":"pong"}
    

Other configurations

Review other common configurations.

Use a ConfigMap for the proto descriptor

For large or frequently-updated descriptors, store the descriptor in a Kubernetes ConfigMap so you can update it independently of the policy. The control plane watches the ConfigMap and automatically rebuilds the gateway configuration when the descriptor changes.

  1. Create a ConfigMap with the proto descriptor. Store the binary bytes in binaryData (used verbatim) or the base64-encoded string in data.

    kubectl create configmap yages-proto-descriptor \
      -n kgateway-system \
      --from-file=descriptor.pb=service.pb
    kubectl apply -f - <<EOF
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: yages-proto-descriptor
      namespace: kgateway-system
      labels:
        app: grpc-echo
    data:
      descriptor: ${PROTO_DESCRIPTOR}
    EOF
  2. Update the EnterpriseKgatewayTrafficPolicy to reference the ConfigMap instead of the inline descriptor.

    kubectl apply -f - <<EOF
    apiVersion: enterprisekgateway.solo.io/v1alpha1
    kind: EnterpriseKgatewayTrafficPolicy
    metadata:
      name: grpc-transcoding-policy
      namespace: kgateway-system
      labels:
        app: grpc-echo
    spec:
      targetRefs:
      - group: gateway.networking.k8s.io
        kind: HTTPRoute
        name: grpc-transcoding-route
      entGrpcJsonTranscoder:
        protoDescriptorConfigMap:
          name: yages-proto-descriptor
          key: descriptor
        services:
        - yages.Echo
        autoMapping: true
        convertGrpcStatus: true
    EOF

    If the ConfigMap is in a different namespace, set the protoDescriptorConfigMap.namespace field. Cross-namespace references require a ReferenceGrant.

Disable transcoding for specific routes

If you apply a transcoding policy at the Gateway level and want to opt a specific HTTPRoute out of transcoding, use disable: {} in a policy that targets that route.

kubectl apply -f - <<EOF
apiVersion: enterprisekgateway.solo.io/v1alpha1
kind: EnterpriseKgatewayTrafficPolicy
metadata:
  name: no-transcoding
  namespace: kgateway-system
spec:
  targetRefs:
  - group: gateway.networking.k8s.io
    kind: HTTPRoute
    name: my-plain-route
  entGrpcJsonTranscoder:
    disable: {}
EOF

Cleanup

You can optionally remove the resources that you set up as part of this guide.
kubectl delete -A httproutes,EnterpriseKgatewayTrafficPolicy,deployments,services,configmaps -l app=grpc-echo
Was this page helpful?