Most products need the same API in browsers and for third parties, not just between services.
Browsers cannot control HTTP/2 framing or read trailers from fetch, so they cannot speak native gRPC. Options:
- gRPC-Web: a variant protocol that browsers can use, translated to gRPC by a proxy (Envoy has a built-in filter) or supported directly by some servers. It supports unary and server-streaming calls, not client or bidirectional streaming.
- Connect (connectrpc.com): a protocol and set of libraries from Buf. Connect servers speak gRPC, gRPC-Web and the Connect protocol, a simple HTTP POST with JSON or binary bodies that you can even call with curl. Generated TypeScript clients work in browsers without a proxy.
- HTTP/JSON transcoding: annotate methods with
google.api.httpoptions (get: "/v1/users/{id}") and let grpc-gateway (Go), Envoy's transcoder or cloud API gateways expose a REST API that maps onto the gRPC service. You can also generate OpenAPI from the same annotations.
Protobuf has a canonical JSON mapping: lowerCamelCase field names, 64-bit integers as strings, enums as names, Timestamp as RFC 3339 strings. Gateways and tools rely on it, so learn its rules.
Choose based on consumers: Connect for your own web and mobile apps, transcoding for public REST APIs, gRPC-Web when an Envoy-based stack is already in place.
Example
syntax = "proto3";
package users.v1;
import "google/api/annotations.proto";
service UserService {
rpc GetUser(GetUserRequest) returns (User) {
option (google.api.http) = { get: "/v1/users/{id}" };
}
rpc CreateUser(CreateUserRequest) returns (User) {
option (google.api.http) = { post: "/v1/users" body: "user" };
}
}
message GetUserRequest { string id = 1; }
message CreateUserRequest { User user = 1; }
message User { string id = 1; string display_name = 2; int64 karma = 3; }
// GET /v1/users/42 via a transcoding gateway returns canonical JSON:
// { "id": "42", "displayName": "Ann", "karma": "1200" } <- int64 as stringRemember
- Browsers need gRPC-Web or Connect, not native gRPC
- Connect servers speak gRPC, gRPC-Web and a simple HTTP protocol
google.api.httpannotations drive REST transcoding- Proto JSON: camelCase names, int64 as strings
Watch out for
- Expecting client-streaming or bidirectional calls to work over gRPC-Web.