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
- An API7 Enterprise instance is running.
- A Gateway Group is created and a Gateway instance is running.
- A token from the Dashboard.
- Docker is installed where the sample upstream will run.
grpcurlis installed for native gRPC verification.- ADC is installed and connected to the target Gateway Group if you use the ADC configurations.
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:
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.
- Admin API
- ADC
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"
}'
Create the Service and Route:
services:
- name: grpc-native-service
labels:
docs-example: grpc-native
upstream:
scheme: grpc
type: roundrobin
nodes:
- host: grpc-service
port: 50051
weight: 100
routes:
- name: grpc-native-route
uris:
- /helloworld.Greeter/SayHello
methods:
- POST
Preview changes owned by this example and confirm that the diff contains no unintended updates or deletions:
adc diff -f adc.yaml \
--include-resource-type service \
--label-selector docs-example=grpc-native
Synchronize the reviewed service configuration:
adc sync -f adc.yaml \
--include-resource-type service \
--label-selector docs-example=grpc-native
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:
- Admin API
- ADC
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"
}
}
}'
ADC cannot create proto resources. Run the Admin API proto command above before synchronizing this Service and Route.
Create the Service and Route:
services:
- name: grpc-transcode-service
labels:
docs-example: grpc-transcode
upstream:
scheme: grpc
type: roundrobin
nodes:
- host: grpc-service
port: 50051
weight: 100
routes:
- name: grpc-transcode-route
uris:
- /hello
methods:
- POST
plugins:
grpc-transcode:
_state: enabled
proto_id: helloworld-proto
service: helloworld.Greeter
method: SayHello
Preview changes owned by this example and confirm that the diff contains no unintended updates or deletions:
adc diff -f adc.yaml \
--include-resource-type service \
--label-selector docs-example=grpc-transcode
Synchronize the reviewed service configuration:
adc sync -f adc.yaml \
--include-resource-type service \
--label-selector docs-example=grpc-transcode
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.
- Admin API
- ADC
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 the Service and Route:
services:
- name: grpc-web-service
labels:
docs-example: grpc-web
upstream:
scheme: grpc
type: roundrobin
nodes:
- host: grpc-service
port: 50051
weight: 100
routes:
- name: grpc-web-route
uris:
- /helloworld.Greeter/SayHello
methods:
- POST
- OPTIONS
plugins:
grpc-web:
_state: enabled
cors_allow_headers: "content-type,x-grpc-web,x-user-agent"
Preview changes owned by this example and confirm that the diff contains no unintended updates or deletions:
adc diff -f adc.yaml \
--include-resource-type service \
--label-selector docs-example=grpc-web
Synchronize the reviewed service configuration:
adc sync -f adc.yaml \
--include-resource-type service \
--label-selector docs-example=grpc-web
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:
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:
<!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
- Configure WebSocket Proxying — learn how to handle long-lived WebSocket connections.
- Configure GraphQL Proxying — explore GraphQL-aware traffic management.