Skip to content
back to writing
8 min readgrpc · protobuf · api-design

Understanding gRPC in Depth: Protobuf, REST, and GraphQL

A junior-friendly deep dive into gRPC, Protocol Buffers, and how gRPC compares with REST and GraphQL in real backend systems.

RG
Rahul Gupta
Senior Software Engineer
share

If you are early in backend engineering, gRPC can feel intimidating.

The words around it sound heavy:

  • Protocol Buffers
  • HTTP/2
  • unary RPC
  • streaming
  • code generation
  • service contracts

But the core idea is not that complicated.

gRPC is a way for one service to call another service like a normal function, even though both services are running on different machines.

Instead of thinking:

Text
send HTTP request to /users/123
parse JSON response
manually map fields

gRPC wants you to think:

Text
userService.GetUser({ id: "123" })

That is the main mental model.

It makes remote service calls feel closer to normal function calls, while still going over the network.

1. Why do we need gRPC at all?

Most developers learn REST first.

REST is easy to understand:

HTTP
GET /users/123
POST /orders
PUT /products/42
DELETE /sessions/current

This works well for many systems. But as services grow, teams start facing problems:

  • different services disagree on request/response shape
  • JSON payloads become large
  • API contracts are not strict enough
  • clients duplicate request-building logic
  • service-to-service calls need better performance
  • streaming use cases are awkward over normal REST

gRPC was built to solve many of these problems.

It gives you:

  • strongly defined service contracts
  • generated client and server code
  • efficient binary payloads
  • HTTP/2 support
  • built-in support for streaming

That is why gRPC is common in microservices, internal platform systems, service mesh control planes, and high-performance backend communication.

2. The simplest gRPC mental model

Imagine you have two services:

Text
order-service
payment-service

When someone places an order, order-service needs to ask payment-service to charge the customer.

In REST, order-service might call:

HTTP
POST /payments/charge

with JSON:

JSON
{
  "orderId": "ord_123",
  "amount": 999,
  "currency": "INR"
}

In gRPC, you define a service method:

PROTO
service PaymentService {
  rpc Charge(ChargeRequest) returns (ChargeResponse);
}

Then your code calls it like a method:

TypeScript
const response = await paymentClient.charge({
  orderId: "ord_123",
  amount: 999,
  currency: "INR",
});

Behind the scenes, it still sends data over the network.

But for the developer, the contract is clearer and the client code is generated from the service definition.

3. What are Protocol Buffers?

Protocol Buffers, usually called protobuf, are the default data format used by gRPC.

Think of protobuf as a strict schema for your API messages.

In REST, you often send JSON like this:

JSON
{
  "id": "user_123",
  "name": "Rahul",
  "age": 28
}

JSON is human-readable, but it does not enforce a strict type contract by itself.

With protobuf, you define the shape first:

PROTO
message User {
  string id = 1;
  string name = 2;
  int32 age = 3;
}

That file says:

  • id must be a string
  • name must be a string
  • age must be an integer

The numbers 1, 2, and 3 are field tags. They are used in the binary encoding. They are not default values.

This is important because protobuf is not just a documentation format. It is used to generate code.

From one .proto file, you can generate:

  • Go structs
  • Java classes
  • TypeScript types
  • Python classes
  • client code
  • server interfaces

That is one of the biggest benefits of gRPC.

The contract becomes a source of truth.

4. Why protobuf is faster than JSON

JSON is text.

Protobuf is binary.

That means protobuf messages are usually:

  • smaller on the wire
  • faster to parse
  • stricter in type handling
  • better for service-to-service communication

Example JSON:

JSON
{
  "id": "user_123",
  "name": "Rahul",
  "age": 28
}

JSON includes field names like "id", "name", and "age" every time.

Protobuf uses numeric field tags internally, so it does not need to send full field names repeatedly.

That saves space and parsing work.

This does not mean JSON is bad. JSON is excellent for public APIs, debugging, browsers, and human-readable payloads.

But for backend-to-backend communication, protobuf is often more efficient.

5. What does HTTP/2 have to do with gRPC?

gRPC usually runs over HTTP/2.

HTTP/2 gives gRPC useful features:

  • multiplexing multiple requests over one connection
  • better connection reuse
  • binary framing
  • support for streaming
  • lower overhead for many service-to-service calls

You do not need to understand every HTTP/2 detail at the start.

Just remember this:

  • REST commonly uses HTTP with JSON
  • gRPC commonly uses HTTP/2 with protobuf

That combination makes gRPC a strong fit for internal high-performance service communication.

6. The four types of gRPC calls

gRPC is not only request-response.

It supports four call styles.

7. Unary RPC

Client sends one request. Server sends one response.

Text
client -> request -> server
client <- response <- server

Example:

PROTO
rpc GetUser(GetUserRequest) returns (User);

Use this for normal service calls like:

  • get user
  • create order
  • verify payment
  • fetch config

This is the gRPC style most similar to REST.

8. Server streaming

Client sends one request. Server sends many responses.

Text
client -> request -> server
client <- item 1
client <- item 2
client <- item 3

Example:

PROTO
rpc WatchOrders(WatchOrdersRequest) returns (stream OrderEvent);

Use this when the client wants to keep receiving updates:

  • live order status
  • logs
  • notifications
  • config updates

9. Client streaming

Client sends many messages. Server sends one response.

Text
client -> item 1
client -> item 2
client -> item 3
client <- final response

Example:

PROTO
rpc UploadMetrics(stream Metric) returns (UploadSummary);

Use this when the client needs to upload many items efficiently:

  • metrics
  • logs
  • file chunks
  • sensor data

10. Bidirectional streaming

Both client and server can send messages continuously.

Text
client <-> server

Example:

PROTO
rpc Chat(stream ChatMessage) returns (stream ChatMessage);

Use this for:

  • chat systems
  • live collaboration
  • real-time control channels
  • long-running data sync

This is one of the places gRPC feels much stronger than normal REST.

11. gRPC vs REST

REST is resource-oriented.

You think in URLs:

HTTP
GET /users/123
POST /orders

gRPC is action/service-oriented.

You think in methods:

PROTO
rpc GetUser(GetUserRequest) returns (User);
rpc CreateOrder(CreateOrderRequest) returns (Order);

Both can solve many of the same problems, but they optimize for different things.

12. REST pros

REST is great because:

  • easy to learn
  • works naturally in browsers
  • easy to test with curl/Postman
  • JSON is human-readable
  • great for public APIs
  • caching with HTTP semantics is straightforward
  • many developers already understand it

If you are building an API for external developers, REST is often the safest default.

13. REST cons

REST can become painful when:

  • contracts are loosely enforced
  • clients manually duplicate types
  • payloads are large
  • service-to-service communication is very frequent
  • streaming is needed
  • different teams interpret API structure differently

REST can be excellent, but large systems need discipline around schemas, versioning, and documentation.

14. gRPC pros

gRPC is strong because:

  • service contracts are explicit
  • client/server code can be generated
  • protobuf payloads are compact
  • type safety is better
  • streaming is built in
  • service-to-service performance is usually strong
  • good fit for internal microservices

If two backend services talk frequently, gRPC can be a very good choice.

15. gRPC cons

gRPC also has real downsides:

  • harder to debug by just reading payloads
  • not as browser-friendly as REST without extra tooling
  • .proto generation adds build complexity
  • teams must understand schema evolution
  • public API consumers may find REST easier
  • some infrastructure tools are more HTTP/JSON-friendly

The biggest beginner mistake is assuming gRPC is automatically better because it is faster.

It is not automatically better.

It is better when its tradeoffs fit the system.

16. gRPC vs GraphQL

GraphQL solves a different problem.

GraphQL is mainly about letting clients ask for exactly the data they need.

Example:

GRAPHQL
query {
  user(id: "123") {
    name
    orders {
      id
      total
    }
  }
}

This is very useful for frontend-heavy products where different screens need different data shapes.

gRPC is usually stronger for backend-to-backend service contracts.

So the comparison is not “which one is better?”

It is more like:

  • GraphQL is client-query focused
  • gRPC is service-contract focused
  • REST is resource/API focused

17. GraphQL pros

GraphQL is useful because:

  • clients choose exactly what fields they need
  • avoids over-fetching and under-fetching
  • good for complex frontend data requirements
  • one endpoint can expose a rich graph of data
  • strongly typed schema helps frontend/backend collaboration

It is often a great fit when product UI needs flexible data access.

18. GraphQL cons

GraphQL can become difficult because:

  • caching is harder than simple REST caching
  • query complexity can hurt performance
  • authorization can become subtle
  • backend resolvers may create N+1 query problems
  • operating GraphQL safely needs guardrails

GraphQL gives clients power. That power needs limits.

19. When should you choose REST?

Choose REST when:

  • you are building public APIs
  • browser support matters
  • human readability matters
  • the API maps naturally to resources
  • simple request-response is enough
  • team familiarity matters more than raw efficiency

Example:

  • public payment API
  • user management API
  • admin CRUD API
  • partner integration API

REST is boring in the best possible way.

20. When should you choose gRPC?

Choose gRPC when:

  • backend services call each other frequently
  • strict contracts matter
  • performance matters
  • streaming is useful
  • clients and servers are controlled by your organization
  • you can manage protobuf generation cleanly

Example:

  • internal microservices
  • service mesh control planes
  • low-latency backend systems
  • high-throughput data pipelines
  • real-time server communication

gRPC is excellent when the main consumers are other services, not random humans with curl.

21. When should you choose GraphQL?

Choose GraphQL when:

  • frontend screens need flexible data shapes
  • clients often need nested related data
  • avoiding over-fetching matters
  • multiple clients need different views of the same domain

Example:

  • web/mobile app backend
  • product dashboards
  • content-heavy applications
  • complex frontend aggregation layers

GraphQL is usually more about product data access than internal service performance.

22. Schema evolution: the part juniors should not ignore

With protobuf, you must be careful when changing messages.

This is safe:

PROTO
message User {
  string id = 1;
  string name = 2;
  int32 age = 3;
  string email = 4;
}

Adding a new field is usually okay.

This is dangerous:

PROTO
message User {
  string id = 1;
  int32 name = 2;
}

Changing the meaning or type of an existing field can break clients.

Basic protobuf rule:

  • do not reuse field numbers
  • do not casually change field types
  • add new fields instead of changing old ones
  • keep backward compatibility in mind

This is one of the most important habits when working with gRPC.

23. A simple decision table

Use caseBest default
Public API for external usersREST
Internal service-to-service callsgRPC
Frontend needs flexible nested dataGraphQL
Real-time server streaminggRPC
Simple CRUD admin APIREST
Mobile app with varied screen dataGraphQL or REST
High-throughput backend pipelinegRPC

This table is not a law. It is a starting point.

24. The practical architecture pattern

Many mature systems use more than one API style.

For example:

Text
mobile app -> GraphQL or REST -> backend-for-frontend
backend-for-frontend -> gRPC -> internal services
internal services -> gRPC -> other internal services
external partners -> REST -> public API gateway

That is normal.

You do not need to force one protocol everywhere.

Use the right protocol at the right boundary.

25. The final mental model

If you remember only one thing:

  • REST is great for simple, public, human-friendly APIs
  • GraphQL is great when clients need flexible data selection
  • gRPC is great for strongly typed, efficient service-to-service communication
  • protobuf is the schema and binary message format that makes gRPC efficient and type-safe

gRPC is not magic.

It is a contract-first way to make services talk to each other efficiently.

Once that clicks, the rest becomes much less scary.


If you are a junior engineer learning gRPC, do not start by memorizing every streaming mode or HTTP/2 detail. Start by understanding .proto files, generated clients, and why strict contracts matter. That foundation will make everything else easier.

Rahul Gupta
share