Back to API Design Best Practices
Q10Question 10 of 22

What is gRPC and when should you use gRPC vs REST

Key Concepts
  • gRPC uses Protocol Buffers for compact binary serialization and HTTP/2 for multiplexed transport with streaming.
  • A .proto file defines the contract; the toolchain generates typed client and server stubs in many languages.
  • Protobuf is smaller than JSON because field names are numeric tags and values are packed in binary.
  • HTTP/2 multiplexing lets many calls share one connection without head-of-line blocking.
  • gRPC supports four call patterns: unary, server streaming, client streaming, and bidirectional streaming.
  • Use gRPC for internal high-throughput service-to-service calls; use REST for public APIs where simplicity matters.

Definition

gRPC is binary Protocol Buffers over HTTP/2 with generated typed stubs and streaming, ideal for fast internal service-to-service calls, while REST wins for simple, readable, ubiquitous public APIs.
  • gRPC – a high-performance remote procedure call framework that uses Protocol Buffers as a compact binary serialization format and HTTP/2 as the transport.
  • Protocol Buffers (protobuf) – a binary encoding where field names are replaced by numeric tags and values are packed, making payloads smaller and faster to serialize than JSON.
  • HTTP/2 – provides multiplexing (many calls on one connection), header compression, and bidirectional streaming.
  • Code generation – from one .proto file, typed client and server stubs are generated in Java, Go, Python, and other languages, ensuring the contract cannot drift.
  • Streaming – gRPC supports unary, server streaming, client streaming, and bidirectional streaming patterns.

Inside National Bank of India, the transfer department and the fraud department talk hundreds of thousands of times a second. They do not send handwritten letters in plain English; they use a tight, agreed shorthand on a fast dedicated phone line, where both sides know the exact format and a single call can carry many messages at once. That shorthand on the fast line is gRPC. But when the bank talks to the outside world, it uses plain, readable forms that anyone understands without special tools, and that is REST.

Understanding the Concepts

What is gRPC?

gRPC starts from a contract written in a .proto file that defines services, methods, and message types. From this one file, the toolchain generates strongly typed client and server code in many languages, so a Java service and a Go service share the exact same contract and cannot drift.

PROTO
syntax = "proto3";

package com.bank;

service TransferService {
  rpc Transfer (TransferRequest) returns (TransferReply);
  rpc StreamStatement (AccountRef) returns (stream Transaction);
}

message TransferRequest {
  string from_account = 1;
  string to_account = 2;
  int64 amount_paise = 3;
}

message TransferReply {
  string transfer_id = 1;
  string status = 2;
}

message AccountRef {
  string account_number = 1;
}

message Transaction {
  string txn_id = 1;
  int64 amount_paise = 2;
  string type = 3;
}

Why is protobuf smaller and faster than JSON?

Protobuf replaces field names with numeric tags on the wire. Values are packed in binary rather than text. This reduces payload size and CPU cost of serialization.

TEXT
// JSON (67 bytes, text):
{"fromAccount":"SAV2001","toAccount":"CUR3001","amountPaise":5000000}

// Protobuf (approx 25 bytes, binary):
0A 07 53 41 56 32 30 30 31  12 07 43 55 52 33 30 30 31  18 80 89 97 02
// field 1 (from_account) = SAV2001, field 2 (to_account) = CUR3001, field 3 = 5000000

How does HTTP/2 improve performance?

HTTP/2 multiplexes many calls on one TCP connection without head-of-line blocking. Header compression reduces overhead. And HTTP/2 enables streaming, which REST over HTTP/1.1 does not naturally offer.

DIAGRAM
// HTTP/1.1: one request per connection, or pipelining with head-of-line blocking
Conn 1: req1 → resp1 → req2 → resp2

// HTTP/2: many concurrent streams on one connection
Conn 1: stream1(req1), stream2(req2), stream3(req3) all in flight

How does a generated gRPC stub look in Java?

The generated blocking stub makes a remote call look like a local method call.

JAVA
package com.bank.client;

import com.bank.TransferServiceGrpc;
import com.bank.TransferRequest;
import com.bank.TransferReply;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;

public class TransferClient {
    public static void main(String[] args) {
        ManagedChannel channel = ManagedChannelBuilder
                .forAddress("localhost", 9090)
                .usePlaintext()
                .build();

        TransferServiceGrpc.TransferServiceBlockingStub stub =
                TransferServiceGrpc.newBlockingStub(channel);

        TransferReply reply = stub.transfer(
                TransferRequest.newBuilder()
                        .setFromAccount("SAV2001")
                        .setToAccount("CUR3001")
                        .setAmountPaise(5000000)
                        .build());

        System.out.println("Transfer: " + reply.getTransferId() + " " + reply.getStatus());
        channel.shutdown();
    }
}
TEXT
Transfer: TXF-001 COMPLETED

When should you use gRPC vs REST? (comparison table)

| Aspect | gRPC | REST |
|---|---|---|
| Serialization | Binary protobuf (compact) | Text JSON (readable) |
| Transport | HTTP/2 (multiplexing, streaming) | HTTP/1.1 or HTTP/2 |
| Contract | .proto file, code-generated stubs | OpenAPI spec, no code gen required |
| Human-readable | No (need protobuf tools) | Yes (curl and read) |
| Browser support | Needs gRPC-Web proxy | Native |
| HTTP caching | Not available | URL-based caching |
| Streaming | First-class (4 patterns) | Workarounds (SSE, WebSocket) |
| Best for | Internal high-throughput calls | Public APIs, simplicity |

What are the weaknesses of gRPC?

gRPC payloads are not human-readable, so you cannot curl them. Browser support requires a gRPC-Web proxy. HTTP caching that REST enjoys via URLs is not available. And the barrier to entry is higher than REST.

BASH
# REST – trivial to test:
curl -X POST https://api.bank.com/api/v1/transfers \
  -H "Content-Type: application/json" \
  -d '{"from":"SAV2001","to":"CUR3001","amount":50000}'

# gRPC – needs special tools:
grpcurl -plaintext -d '{"from_account":"SAV2001","to_account":"CUR3001","amount_paise":5000000}' \
  localhost:9090 com.bank.TransferService/Transfer

What are common mistakes?

A common mistake is using gRPC for a public-facing API where partners need to integrate quickly. The partners would need protobuf tooling, which raises the barrier. Another mistake is not setting deadlines on gRPC calls, which can cause cascading timeouts across services.

JAVA
// Wrong – no deadline, call can hang indefinitely:
stub.transfer(request);

// Correct – always set a deadline:
stub.withDeadlineAfter(5, TimeUnit.SECONDS).transfer(request);

How do teams combine gRPC and REST?

A common architecture uses gRPC for the internal microservice mesh and REST at the edge. A gateway translates external REST calls into internal gRPC.

DIAGRAM
  External (partners, browsers):
  REST → API Gateway → gRPC → Internal services
  
  Internal (service-to-service):
  gRPC directly between Transfer, Fraud, Account services

What should a senior engineer know?

A senior engineer knows that gRPC's typed contract from the proto file is valuable in regulated banking because it ensures exact message shapes between critical services. They set deadlines on every call, configure health checks, and use service mesh tools like Envoy that speak gRPC natively. They also know that gRPC supports interceptors for cross-cutting concerns like authentication, logging, and distributed tracing, similar to Spring filters for REST.

Visual Overview

DIAGRAM
  gRPC Architecture:

  .proto file (shared contract)
       │
       ├──→ Java stubs (generated)
       ├──→ Go stubs (generated)
       └──→ Python stubs (generated)

  ┌─────────────┐   protobuf/HTTP/2   ┌─────────────┐
  │ Transfer    │ ════════════════════ │ Fraud       │
  │ Service     │   binary, fast,      │ Service     │
  │ (Java)      │   multiplexed        │ (Go)        │
  └─────────────┘                      └─────────────┘

  External clients:
  ┌──────┐   REST/JSON   ┌─────────┐   gRPC   ┌──────────┐
  │Client│ ─────────────→│ Gateway │ ════════→│ Internal │
  └──────┘               └─────────┘          └──────────┘

Key Takeaway

gRPC is a high-performance remote procedure call framework using Protocol Buffers for compact binary serialization and HTTP/2 for multiplexed transport with streaming. You define a contract in a .proto file and the toolchain generates typed client and server stubs in many languages, so a remote call looks like a local method call and the two sides cannot drift, because the types are enforced at compile time.

Protobuf is smaller than JSON because field names are replaced by numeric tags and values are packed in binary, and HTTP/2 adds multiplexing so many calls share one connection, plus header compression and first-class streaming. The strengths are performance and contract safety, making gRPC excellent for dense internal traffic between microservices.

The weaknesses are that it is not human-readable, browsers need a gRPC-Web proxy, and URL-based HTTP caching is lost. At National Bank of India, the transfer service checks the fraud service and the account service on every transfer at high volume. The team moved the internal mesh to gRPC, cutting payload size and latency, and gaining a typed contract that fails at build time rather than in production. At the edge they kept REST so fintech partners integrate from any language, with a gateway translating REST to gRPC.

The decision rule is to use gRPC for internal service-to-service calls where latency, throughput, and strict typed contracts matter, and REST for public APIs where human readability, browser support, and HTTP caching matter. In practice, run both: a fast typed gRPC mesh inside and a simple REST edge outside.

Remember one sentence: gRPC is the fast binary shorthand between your own services, while REST is the plain-language form for the outside world.

---

Share
Use ← → arrow keys to navigate