Skip to main content
Version: 3.9.x

Configure gRPC Proxying

API7 Enterprise supports proxying gRPC traffic, allowing you to manage gRPC services with the same traffic management, security, and observability features as your HTTP services. You can also transcode RESTful HTTP requests to gRPC and enable gRPC-Web support for browser-based clients.

Prerequisites​

Start a Sample gRPC Server​

Set GATEWAY_CONTAINER to the running API7 Gateway container. Create a dedicated network and connect the gateway to it:

export GATEWAY_CONTAINER=replace-with-gateway-container-name

docker network create gateway-grpc-net
docker network connect gateway-grpc-net "$GATEWAY_CONTAINER"

Start the maintained sample gRPC server on the shared network:

docker run -d \
--name grpc-service \
--network gateway-grpc-net \
-p 50051:50051 \
api7/grpc-server-example:1.0.2

Download the matching protocol definition:

curl -fLo helloworld.proto \
"https://raw.githubusercontent.com/api7/grpc_server_example/release/1.0.2/proto/helloworld.proto"

The server exposes the helloworld.Greeter service and the SayHello method. Verify the upstream directly:

grpcurl -plaintext \
-proto helloworld.proto \
-d '{"name":"API7"}' \
127.0.0.1:50051 \
helloworld.Greeter.SayHello

For a non-Docker gateway deployment, run the sample server where the gateway can reach it and replace grpc-service in the upstream configurations with that address.

Enable an HTTP/2 Listener for Native gRPC​

Native gRPC clients require an HTTP/2 listener that they can reach. REST-to-gRPC and gRPC-Web clients can use the normal HTTP listener because the gateway translates those requests before calling the native gRPC upstream.

Expose an additional listener, such as port 9081, from the gateway deployment. In the complete config.yaml used by the deployment, preserve the existing settings and add the listeners:

config.yaml
apisix:
node_listen:
- port: 9080
- port: 9081
enable_http2: true

Restart or recreate the gateway through its normal deployment workflow, then use port 9081 for native gRPC validation.

Recreating a Docker gateway removes the manual network attachment created earlier. Set GATEWAY_CONTAINER to the new container name and reconnect it before continuing:

export GATEWAY_CONTAINER=replace-with-recreated-gateway-container-name
docker network connect gateway-grpc-net "$GATEWAY_CONTAINER"

Configure Admin API Access​

The Admin API examples use a Dashboard token. Export the token and replace {gateway_group_id} with the target Gateway Group ID in each request URL. The quickstart Gateway Group uses default.

export API_KEY=replace-with-dashboard-token

Proxy Native gRPC​

Create a Service with the grpc scheme and a Route that points to it. Use the grpcs scheme instead when the upstream accepts gRPC over TLS.

Create the Service:

curl -k "https://localhost:7443/apisix/admin/services/grpc-native-service?gateway_group_id={gateway_group_id}" -X PUT \
-H "X-API-KEY: ${API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"name": "grpc-native-service",
"upstream": {
"type": "roundrobin",
"scheme": "grpc",
"nodes": [
{
"host": "grpc-service",
"port": 50051,
"weight": 100
}
]
}
}'

Create the Route:

curl -k "https://localhost:7443/apisix/admin/routes/grpc-native-route?gateway_group_id={gateway_group_id}" -X PUT \
-H "X-API-KEY: ${API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"name": "grpc-native-route",
"methods": ["POST"],
"paths": ["/helloworld.Greeter/SayHello"],
"service_id": "grpc-native-service"
}'

Send a native gRPC request through the HTTP/2 listener:

grpcurl -plaintext \
-proto helloworld.proto \
-d '{"name":"API7"}' \
127.0.0.1:9081 \
helloworld.Greeter.SayHello

The response should contain:

{
"message": "Hello API7"
}

Configure REST-to-gRPC Transcoding​

The grpc-transcode plugin exposes a gRPC method to HTTP clients and converts between JSON and Protocol Buffers.

Upload the Proto Definition​

Proto resources must be created through the Admin API. Install jq to encode the downloaded file as the request body:

jq -Rs '{content: .}' helloworld.proto | \
curl -k "https://localhost:7443/apisix/admin/protos/helloworld-proto?gateway_group_id={gateway_group_id}" -X PUT \
-H "X-API-KEY: ${API_KEY}" \
-H "Content-Type: application/json" \
-d @-

Configure the Transcoding Route​

Create a separate Service and Route for the transcoding example:

Create the Service:

curl -k "https://localhost:7443/apisix/admin/services/grpc-transcode-service?gateway_group_id={gateway_group_id}" -X PUT \
-H "X-API-KEY: ${API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"name": "grpc-transcode-service",
"upstream": {
"type": "roundrobin",
"scheme": "grpc",
"nodes": [
{
"host": "grpc-service",
"port": 50051,
"weight": 100
}
]
}
}'

Create the Route:

curl -k "https://localhost:7443/apisix/admin/routes/grpc-transcode-route?gateway_group_id={gateway_group_id}" -X PUT \
-H "X-API-KEY: ${API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"name": "grpc-transcode-route",
"methods": ["POST"],
"paths": ["/hello"],
"service_id": "grpc-transcode-service",
"plugins": {
"grpc-transcode": {
"proto_id": "helloworld-proto",
"service": "helloworld.Greeter",
"method": "SayHello"
}
}
}'

Send an HTTP request to the transcoding route:

curl "http://127.0.0.1:9080/hello" -X POST \
-H "Content-Type: application/json" \
-d '{"name":"API7"}'

The gateway should return the decoded gRPC response:

{
"items": [],
"message": "Hello API7",
"gender": "GENDER_UNKNOWN"
}

Configure gRPC-Web​

The grpc-web plugin lets browser clients call a native gRPC upstream. Create a separate Service and Route for browser traffic. The browser client runs on a different port, so the Route must accept both POST requests and OPTIONS preflight requests.

Create the Service:

curl -k "https://localhost:7443/apisix/admin/services/grpc-web-service?gateway_group_id={gateway_group_id}" -X PUT \
-H "X-API-KEY: ${API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"name": "grpc-web-service",
"upstream": {
"type": "roundrobin",
"scheme": "grpc",
"nodes": [
{
"host": "grpc-service",
"port": 50051,
"weight": 100
}
]
}
}'

Create the Route:

curl -k "https://localhost:7443/apisix/admin/routes/grpc-web-route?gateway_group_id={gateway_group_id}" -X PUT \
-H "X-API-KEY: ${API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"name": "grpc-web-route",
"methods": ["POST", "OPTIONS"],
"paths": ["/helloworld.Greeter/SayHello"],
"service_id": "grpc-web-service",
"plugins": {
"grpc-web": {
"cors_allow_headers": "content-type,x-grpc-web,x-user-agent"
}
}
}'

Create a Browser Client​

Install Node.js, the Protocol Buffers compiler, and protoc-gen-grpc-web to generate the client. The final step uses Python 3 to serve the page locally.

Generate a browser client for the same helloworld.Greeter contract used by the Route.

Create a client directory and install the pinned browser dependencies:

mkdir grpc-web-client
cp helloworld.proto grpc-web-client/
cd grpc-web-client

npm init -y
npm install --save-exact grpc-web@2.1.1 google-protobuf@4.0.2
npm install --save-dev --save-exact \
@protocolbuffers/protoc-gen-js@4.0.2 \
esbuild@0.25.9

Generate the JavaScript message classes and client stub:

protoc -I=. \
--plugin=protoc-gen-js=./node_modules/.bin/protoc-gen-js \
--js_out=import_style=commonjs:. \
--grpc-web_out=import_style=commonjs,mode=grpcwebtext:. \
helloworld.proto

Create the browser client:

client.js
const { GreeterClient } = require('./helloworld_grpc_web_pb');
const { HelloRequest } = require('./helloworld_pb');

const output = document.querySelector('#output');
const client = new GreeterClient('http://127.0.0.1:9080');
const request = new HelloRequest();
request.setName('API7');

client.sayHello(request, {}, (error, response) => {
output.textContent = error
? `error: ${error.message}`
: response.getMessage();
});

Create a page that displays the result:

index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>API7 Gateway gRPC-Web example</title>
</head>
<body>
<pre id="output"></pre>
<script src="bundle.js"></script>
</body>
</html>

Bundle and serve the client:

./node_modules/.bin/esbuild client.js --bundle --outfile=bundle.js
python3 -m http.server 8088

Open http://127.0.0.1:8088 in a browser. The page should display:

Hello API7

Next Steps​