Intermediate 3 min · March 30, 2026

gRPC vs REST — Unbounded Stream Buffers Cause OOM

gRPC streaming caused 4GB heap OOM every 6 hours from unbounded buffers in production.

N
Naren · Founder
Plain-English first. Then code. Then the interview question.
About
Quick Answer
  • gRPC uses Protocol Buffers (binary) over HTTP/2; REST uses JSON over HTTP/1.1 or HTTP/2
  • gRPC supports four streaming patterns natively; REST needs workarounds like WebSockets
  • gRPC is 2-5x faster for frequent small-payload calls; REST is baseline
  • gRPC requires code-generated clients; REST works with any HTTP client
  • Biggest mistake: using gRPC for public APIs without a REST gateway

I've built and maintained both REST and gRPC APIs in production. The decision is rarely about technical superiority — both work. It's about the consumers and their requirements. External API consumed by customers? REST. Internal microservice-to-microservice communication that processes 50,000 RPCs per second? gRPC. The mistake I see is teams defaulting to one or the other without asking 'who is consuming this and what do they need?'

In 2022, we migrated an internal order-processing service from REST to gRPC. The service handled inter-service communication between six microservices, averaging 30,000 requests per minute. After migration: 40% reduction in serialisation overhead, 60% reduction in request latency at P99. The consumer apps stayed on REST. The internal fabric went gRPC. Both in production, each doing what it's good at.

Protocol and Serialisation: Where the Performance Difference Comes From

REST typically sends JSON over HTTP/1.1. JSON is text — human readable, but verbose. Every field name is repeated as a string in every message. HTTP/1.1 opens a new TCP connection per request (or reuses one with keep-alive, but still has head-of-line blocking).

gRPC uses Protocol Buffers (protobuf) over HTTP/2. Protobuf is binary — fields are identified by integer tags, not string names. The same data that takes 200 bytes as JSON might take 40 bytes as protobuf. HTTP/2 multiplexes multiple requests over a single TCP connection and supports full-duplex streaming.

The performance difference is real but often overstated in benchmarks that don't reflect production conditions. The 3-5x latency improvement cited for gRPC over REST usually holds for high-frequency, small-payload inter-service calls. For large payloads or infrequent calls, the difference shrinks.

REST: Why It Still Wins for Most APIs

REST's dominance for public and external APIs isn't about technical merit — it's about ubiquity. Every HTTP client in every language speaks REST. Browsers speak REST natively. curl speaks REST. Postman speaks REST. Your customers' mobile app developers know REST. Your partners' integration teams know REST.

gRPC requires a generated client from the .proto definition. Your API consumers need to run protoc, add gRPC dependencies, and handle the generated code. For internal services under your control, this is a minor inconvenience. For external API consumers who might be using PHP, Ruby, or a legacy Java stack, it's a barrier.

REST also wins on tooling maturity: API gateways, load balancers, proxies, observability tools, and browsers all understand HTTP/JSON natively. gRPC on HTTP/2 requires specific support at every layer.

Streaming: The Capability REST Can't Match

gRPC's streaming support is its genuinely unique capability. REST over HTTP/1.1 is inherently request-response. Long polling, WebSockets, and Server-Sent Events are REST workarounds for streaming — they work but they're not native to the protocol.

Unary: one request, one response. Same as REST.

Server streaming: one request, stream of responses. Useful for: real-time notifications, progress updates, large dataset pagination without polling.

Client streaming: stream of requests, one response. Useful for: bulk ingestion, file upload with progress tracking.

Bidirectional streaming: both sides stream simultaneously. Useful for: real-time chat, collaborative editing, live dashboards.

If your use case involves any of these patterns, gRPC's native streaming is significantly cleaner than working around REST's limitations.

Error Handling and Status Codes: Differences That Matter in Production

REST uses HTTP status codes: 200 for success, 400 for bad request, 404 for not found, 500 for server error. These are well-understood but limited. Custom error messages go in the response body and are not standardised across implementations.

gRPC defines a set of status codes in protobuf (like INVALID_ARGUMENT, NOT_FOUND, INTERNAL, UNAVAILABLE) that are standard across all implementations. Every gRPC response includes a status code and an optional error message. Additionally, gRPC supports rich error models through the google.rpc.Status proto, including error details with structured information.

The critical difference is that gRPC's error model is consistent across all client languages — the same status code means the same thing in Java, Go, or Python. REST depends entirely on documentation and convention.

Tooling and Ecosystem: Where REST Still Leads

REST has decades of tooling maturity. Postman, Insomnia, curl, and browser dev tools all understand HTTP/JSON natively. API gateways like Kong, AWS API Gateway, and Nginx handle REST without configuration. Monitoring tools parse HTTP methods, status codes, and response times out of the box. Load balancers distribute REST requests without special setup.

gRPC tooling has improved but still lags. grpcurl and BloomRPC exist, but they're not as popular as Postman. API gateways need explicit HTTP/2 support and gRPC-web translation for browsers. Monitoring gRPC calls requires special instrumentation: you need the service name, method, and status code — not just HTTP metadata. Some tools now support gRPC natively (like gRPC reflection for service discovery), but the gap is real.

If your team is small or you need to onboard junior engineers quickly, the REST toolchain gives everyone a head start. gRPC requires learning protoc, generated stubs, and a new debugging workflow.

gRPC vs REST at a Glance
AspectRESTgRPC
ProtocolHTTP/1.1 or HTTP/2HTTP/2 only
SerialisationJSON (text)Protocol Buffers (binary)
ContractOpenAPI/Swagger (optional).proto file (required)
Code generationOptionalRequired (client + server stubs)
Browser supportNativeRequires grpc-web proxy
StreamingWorkarounds (SSE, WebSocket)Native (4 patterns)
PerformanceBaseline~2-5x faster for frequent calls
Error handlingHTTP status codes + custom bodyStandardised status codes + rich error model
ToolingUniversal (Postman, curl, etc.)Specialised (BloomRPC, grpcurl)
Best forPublic APIs, external consumersInternal microservices, high-frequency calls

Key Takeaways

  • REST over HTTP/JSON for external APIs, public-facing endpoints, and any consumer you don't control — ubiquity and tooling win.
  • gRPC over protobuf/HTTP/2 for internal microservice communication, high-frequency calls, and when streaming is required.
  • gRPC is 2-5x faster for frequent small-payload calls. For large payloads or infrequent calls, the difference is smaller.
  • The .proto file is the source of truth for a gRPC API — it enforces a strict contract, enables code generation in any language, and prevents the schema drift common in JSON APIs.
  • You don't have to choose: REST gateway for external traffic routing to internal gRPC services is a standard production pattern.

Common Mistakes to Avoid

  • Using gRPC for a public API without a REST/JSON facade
    Symptom: External developers cannot easily consume gRPC without generated clients. They complain about setup complexity. Some abandon the API entirely.
    Fix: Provide a REST gateway that translates HTTP/JSON to protobuf. Deploy gRPC-Gateway or Envoy in front of the gRPC service. Offer both gRPC and REST endpoints documented side by side.
  • Choosing REST for high-frequency internal service calls because 'it's simpler'
    Symptom: At 10,000+ RPCs per minute, CPU usage spikes due to JSON serialisation. P99 latency grows due to HTTP/1.1 connection overhead and head-of-line blocking.
    Fix: Migrate to gRPC for internal services that exchange small payloads frequently. Use protobuf for the serialisation improvement and HTTP/2 for multiplexing. The migration effort pays off within weeks at that scale.
  • Not defining .proto contracts upfront for gRPC
    Symptom: After a few field modifications, the .proto file no longer matches the actual data structure. Field numbers are reused or changed, causing silent data corruption. Clients break at runtime without obvious errors.
    Fix: Treat the .proto file as the single source of truth. Never change field numbers or types after the contract is in use. Use reserved fields for retired fields. Enforce schema validation in CI to detect drift.
  • Ignoring gRPC's built-in deadline/timeout propagation
    Symptom: A chain of microservices (A→B→C) where only A sets a deadline. B and C have no deadline context, so B waits indefinitely for C. A eventually times out, but B is stuck handling a request that A already gave up on. Cascading timeout storms follow.
    Fix: Always propagate the gRPC deadline via the Context. Use Context.current().withDeadlineAfter(...) and pass it to downstream calls. Set a maximum deadline at the edge service. Never create a new Context without inheriting the deadline.

Interview Questions on This Topic

  • QWhat are the main differences between gRPC and REST? When would you choose each?JuniorReveal
    gRPC uses Protocol Buffers over HTTP/2, supports four streaming patterns, requires code-generated clients, and has standardised status codes. REST uses JSON over HTTP/1.1 (or HTTP/2), is request-response only, works with any HTTP client, and uses HTTP status codes. Choose gRPC for internal microservice communication, high-throughput scenarios (>10k req/min), and when you need native streaming. Choose REST for public APIs, any consumer you don't control, browser-facing services, or when onboarding simplicity matters more than raw performance.
  • QExplain how Protocol Buffers differ from JSON and what performance implications that has.Mid-levelReveal
    Protocol Buffers are binary — fields are identified by integer tags, not string names. The same data as JSON takes roughly one-third the bytes. Parsing protobuf is a direct binary read, while JSON requires string tokenization and object construction. For frequent small payloads (<1KB), protobuf is 3-5x faster. For infrequent or large payloads, the benefit narrows because parsing time is dominated by I/O. The catch: protobuf requires a schema and code generation, so it adds a dependency and build step.
  • QWhat are the four communication patterns in gRPC and when would you use each?Mid-levelReveal
    1) Unary: one request, one response — replaces REST endpoints. 2) Server streaming: one request, streamed responses — for real-time notifications, progress updates, pagination without polling. 3) Client streaming: streamed requests, one response — for batch uploads, sensor data ingestion, where client sends multiple items and server responds once. 4) Bidirectional streaming: both sides stream independently — for chat, collaborative editing, real-time dashboards, or any scenario where both sides send data simultaneously. Use server or bidirectional streaming when latency matters and you want to avoid polling. Use client streaming when you want to process data as it arrives instead of waiting for a complete batch.
  • QYou're designing a microservices architecture with 8 internal services and a public API. How would you decide which services use gRPC vs REST?SeniorReveal
    Start by identifying service boundaries: internal services that communicate frequently (high request rate, small payloads) are candidates for gRPC. The public API layer should expose REST endpoints for external consumers. Use a gateway (e.g., gRPC-Gateway) to translate REST to internal gRPC calls. Services that stream data or require real-time updates (like a notification service) benefit from gRPC streaming. Services that rarely communicate or have large payloads (like a reporting service) can stay on REST. The decision should be based on request volume, latency requirements, and consumer type — not a blanket choice.
  • QWhat is gRPC-Gateway and why would you use it?SeniorReveal
    gRPC-Gateway is a tool that generates a reverse proxy server from a .proto file. It exposes a RESTful JSON API that translates HTTP requests to gRPC calls. You'd use it when you want to offer both gRPC and REST interfaces to the same backend without writing separate controllers. It's especially useful when you have internal services using gRPC but external clients require REST. The alternative is manual implementation with something like Envoy's gRPC-JSON transcoder.

Frequently Asked Questions

When should I use gRPC instead of REST?

Use gRPC for internal microservice-to-microservice communication where performance matters, when you need native streaming (server push, client streaming, bidirectional), or when strong schema contracts and code generation are important. Use REST for public APIs, external consumers, browser-facing services, or any situation where you can't control client code generation.

Is gRPC faster than REST?

Yes, typically 2-5x faster for frequent small-payload calls. The advantages come from Protocol Buffers (binary serialisation, smaller payloads than JSON) and HTTP/2 (multiplexing, header compression, no head-of-line blocking). For infrequent calls or large payloads, the performance gap narrows. The difference matters most at high request volumes — 10,000+ RPCs per minute.

Can gRPC and REST coexist in the same system?

Yes, and this is a common production pattern. A REST/JSON API gateway handles external traffic and translates to internal gRPC calls. External consumers get REST ergonomics and universal tooling. Internal services get gRPC performance and streaming. gRPC-Gateway and Envoy proxy both support this pattern out of the box.

Does gRPC work in browsers?

Not natively. Browsers cannot make HTTP/2 requests with the required trailers that gRPC uses. The solution is grpc-web, a modified protocol with a JavaScript client library, combined with an Envoy or Nginx proxy that translates grpc-web to grpc on the server side. For browser-facing APIs, REST remains the simpler choice.

What's the biggest pitfall when implementing gRPC streaming in production?

The biggest pitfall is forgetting backpressure. gRPC's flow control pauses transmission, but it doesn't stop the server from buffering messages in memory. If the client is slow, the server's outbound buffer grows until OOM. Always use bounded queues and set per-stream memory limits. Monitor heap per stream in development. Also be wary of deadlock: if two services use bidirectional streaming and each blocks on the other's response, you get a deadlock. Design for non-blocking processing.

🔥

That's Components. Mark it forged?

3 min read · try the examples if you haven't

Previous
What is a Browser Cache? How It Works and When It Breaks Things
18 / 18 · Components
Next
Microservices Architecture