gRPC is an open-source remote procedure call framework that lets a client call methods on a server as if they were local functions, using Protocol Buffers to define the service contract and HTTP/2 as the transport. Among APIs, it represents the contract-first, strongly typed approach, in which client and server code is generated from one schema file.
Originally developed at Google from its internal Stubby system and now hosted by the Cloud Native Computing Foundation, gRPC is widely used for service-to-service communication in microservice architectures, mobile backends and low-latency systems. Its main advantages are compact binary messages, streaming and generated stubs in more than ten languages; its main costs are weaker browser support and less human-readable traffic.
How gRPC works
A gRPC API starts with a .proto file that declares services, methods and message types. The protoc compiler with language plugins generates a client stub that exposes each method as a function and a server skeleton that developers implement. Calling the stub serializes the request message, sends it to the server and deserializes the response.
On the wire, each call is an HTTP/2 stream. The method is encoded in the path, for example /inventory.v1.StockService/GetItem, metadata travels as HTTP/2 headers, and each message is sent as a length-prefixed binary frame. The call ends with trailers that carry a gRPC status code and an optional message, separate from the HTTP status.
Because HTTP/2 multiplexes many streams over one TCP connection, a client can run hundreds of concurrent calls without opening new connections, and header compression with HPACK reduces repeated metadata overhead. Long-lived connections also mean that load balancing must happen per request rather than per connection, a point that often surprises teams.
The four gRPC call types
gRPC defines four interaction patterns, declared in the .proto file with the stream keyword on the request, the response or both. Streaming is a first-class feature rather than an add-on, which distinguishes gRPC from most request-response API styles.
| Call type | Client sends | Server returns | Typical use |
|---|---|---|---|
| Unary | One message | One message | Lookups, commands, most CRUD calls |
| Server streaming | One message | A stream of messages | Live price feeds, large result sets |
| Client streaming | A stream of messages | One message | Uploading telemetry or file chunks |
| Bidirectional streaming | A stream | A stream | Chat, collaborative editing, real-time control |
Every call can carry a deadline, an absolute time after which the client abandons it and the server receives a cancellation. Deadlines propagate through chains of services, which prevents a slow downstream dependency from tying up resources across the whole system.
gRPC compared with other API styles
gRPC competes with REST APIs, GraphQL and JSON-RPC, and the right choice depends on who the clients are and how tightly they are coupled to the server. The table contrasts the main properties.
| Property | gRPC | REST API | GraphQL | JSON-RPC |
|---|---|---|---|---|
| Contract | Protocol Buffers, mandatory | Optional, often OpenAPI | Schema, mandatory | Informal or custom |
| Payload format | Binary | Usually JSON | JSON | JSON |
| Transport | HTTP/2 | HTTP/1.1 or later | Usually HTTP | Any transport |
| Streaming | Native, both directions | Limited, via SSE or chunking | Subscriptions | Not standardized |
| Browser support | Needs gRPC-Web proxy | Native | Native | Native |
| Caching | No HTTP caching | Strong HTTP caching | Mostly client-side | None |
A frequent architecture therefore uses gRPC internally and REST or GraphQL at the edge. Gateways such as gRPC-Gateway or Envoy transcoding can expose a JSON interface generated from the same .proto definitions, so one contract serves both audiences.
Designing and evolving gRPC services
Protocol Buffers identify fields by numeric tags, not names, and this rule governs safe schema evolution. Adding a new field with a new tag is backward compatible, because older clients simply ignore unknown fields. Changing a tag number or a field type breaks every existing client.
- Never reuse a deleted field number; mark it with
reservedso it cannot return by accident. - Version packages explicitly, such as
payments.v1andpayments.v2, for breaking changes. - Wrap requests and responses in dedicated messages, even for one field, so they can grow later.
- Return rich errors through the standard status codes and detail messages rather than custom flags.
- Lint and check compatibility of
.protofiles in continuous integration before merging.
Operational features matter as much as the schema. Production deployments typically enable TLS or mutual TLS, server reflection for debugging tools, the standard health checking protocol and interceptors, the gRPC equivalent of middleware, for authentication, logging and metrics.
Limitations and common pitfalls
gRPC removes many integration problems but introduces its own operational traps. The table lists the ones experienced teams report most often.
| Problem | Cause | Mitigation |
|---|---|---|
| One backend instance overloaded | Connection-level load balancing with long-lived HTTP/2 | Client-side or proxy request-level balancing |
| Calls hang during outages | No deadline set on the client | Set deadlines on every call |
| Browser cannot call the service | Browsers lack low-level HTTP/2 control | gRPC-Web with a proxy |
| Hard to inspect traffic | Binary payloads | Reflection-aware tools such as grpcurl |
| Memory errors on large payloads | Default message size limit of about 4 MB | Stream data in chunks instead |
gRPC is a weak fit for public APIs consumed by unknown third parties, where JSON over HTTP remains easier to adopt, document and cache. It is a strong fit where both ends are controlled and latency, throughput and type safety dominate the requirements.
Put simply
gRPC is a way for two programs to talk to each other as if one were calling a function inside the other. This works even when they run on different machines. Both sides first agree on a precise menu of what can be asked and what each answer looks like.
Messages travel in a compact form that computers read quickly. They are not plain readable text. The result is fast, reliable conversation between services built by teams that share the same menu. People do need special tools to read the messages.
In practice
A food delivery platform runs about forty internal services. The order service must check stock with the inventory service, price the basket with the pricing service and request a courier from the dispatch service, all within a target of 200 milliseconds for the customer.
The teams define each service in .proto files stored in a shared repository, generate Go and Kotlin clients, and set a 150 millisecond deadline on the order flow that propagates to every downstream call. Courier positions are pushed to the order tracker through server streaming instead of polling. Internal call latency becomes predictable, and breaking contract changes are caught in continuous integration before deployment, while the public mobile API remains a REST interface served by a gateway.