Working with gRPC-Web
1. Understanding gRPC-Web Protocol
| Concept | Detail |
| Need | Browsers can't speak HTTP/2 trailers natively |
| Solution | HTTP/1.1 or HTTP/2 wire with status in body |
| Proxy | Envoy or in-process middleware translates to gRPC |
| Streaming | Server streaming supported; bidi requires WebSocket extension |
2. Installing gRPC-Web Package
Example: npm install
npm install grpc-web google-protobuf
npm install -D protoc-gen-grpc-web ts-proto
3. Configuring grpcwebtext Mode
| Mode | Detail |
| grpcwebtext (default) | Base64-encoded; works without proxy CORS adjustments |
| Larger payload | ~33% overhead |
4. Configuring grpcweb Mode
| Mode | Detail |
| grpcweb (binary) | Raw protobuf; smaller, faster |
| Requires CORS | Allow application/grpc-web+proto content type |
5. Setting Up Envoy Proxy
Example: Envoy filter snippet
http_filters:
- name: envoy.filters.http.grpc_web
- name: envoy.filters.http.cors
- name: envoy.filters.http.router
route_config:
virtual_hosts:
- name: backend
domains: ["*"]
cors:
allow_origin_string_match: [{prefix: "*"}]
allow_methods: GET,POST,OPTIONS
allow_headers: keep-alive,user-agent,cache-control,content-type,content-transfer-encoding,custom-header-1,x-accept-content-transfer-encoding,x-accept-response-streaming,x-user-agent,x-grpc-web,grpc-timeout
expose_headers: grpc-status,grpc-message
6. Generating JavaScript Client
Example: protoc gen js + grpc-web
protoc -I proto \
--js_out=import_style=commonjs:gen/web \
--grpc-web_out=import_style=typescript,mode=grpcweb:gen/web \
proto/user/v1/user.proto
7. Creating Browser Client
Example: TypeScript client call
import { UserServiceClient } from "./gen/web/UserServiceClientPb";
import { GetUserRequest } from "./gen/web/user_pb";
const client = new UserServiceClient("https://api.example.com");
const req = new GetUserRequest();
req.setId("u1");
const user = await client.getUser(req, { authorization: "Bearer " + token });
8. Handling CORS
| Header | Purpose |
Access-Control-Allow-Origin | Allowed origins |
Access-Control-Expose-Headers | Must include grpc-status, grpc-message |
| Preflight | Handle OPTIONS with appropriate methods/headers |
9. Using Unary Calls in Browser
| API | Detail |
| Promise-based | await client.method(req, metadata) |
| Callback | client.method(req, md, (err, res) => {}) |
10. Using Server Streaming in Browser
Example: Server streaming
const stream = client.listEvents(req, {});
stream.on("data", (evt) => console.log(evt.toObject()));
stream.on("status", (s) => console.log("code", s.code));
stream.on("end", () => console.log("done"));
Note: Client streaming and bidi are NOT supported by gRPC-Web spec. Use Connect-Web or WebTransport for these patterns.