🕮6 min read · 1,142 words
Every API starts with the same question: REST or something else? For most web applications, REST wins by default. But as systems grow — microservices communicating internally, real-time data streams, high-performance mobile backends — gRPC becomes increasingly compelling.
This guide gives you the honest comparison so you can make the right call for your specific situation.
What Each Actually Is
REST (Representational State Transfer)
REST is an architectural style, not a protocol. It uses HTTP verbs (GET, POST, PUT, DELETE) to operate on resources identified by URLs. Data is typically JSON. It’s what most developers mean when they say “build an API.”
gRPC (Google Remote Procedure Call)
gRPC is a framework built on HTTP/2 that uses Protocol Buffers (protobuf) for serialization. Instead of resources and HTTP verbs, you define services and methods. You call remote procedures as if they were local function calls. It was open-sourced by Google in 2015 and is widely used in microservices architectures.
The Core Differences
| Aspect | REST | gRPC |
|---|---|---|
| Protocol | HTTP/1.1 or HTTP/2 | HTTP/2 only |
| Data format | JSON (usually) | Protocol Buffers (binary) |
| Contract | Informal (OpenAPI optional) | Strict (.proto file) |
| Streaming | Limited (SSE, WebSockets separate) | Built-in (4 types) |
| Browser support | ✅ Native | ⚠️ Needs gRPC-Web proxy |
| Human readable | ✅ JSON is readable | ❌ Binary format |
| Code generation | Optional (OpenAPI codegen) | Mandatory (from .proto) |
| Learning curve | Low | Medium-High |
| Payload size | Larger (JSON overhead) | 3–10x smaller (binary) |
| Speed | Good | Significantly faster |
Performance — Real Numbers
The performance difference is real and significant at scale:
| Metric | REST/JSON | gRPC/Protobuf |
|---|---|---|
| Serialization time (1000 objects) | ~8ms | ~0.8ms |
| Payload size (same data) | ~1.2KB JSON | ~300 bytes protobuf |
| Throughput (req/s, same server) | ~12,000 | ~65,000 |
| Latency (p99) | ~45ms | ~8ms |
| CPU usage (high load) | Baseline | ~30% lower |
These numbers vary significantly by use case. Simple APIs with small payloads show less difference. APIs with large payloads, high throughput requirements, or many inter-service calls show larger differences.
The gRPC Contract: Protocol Buffers
gRPC’s biggest difference from REST is the mandatory contract — a .proto file that defines your service:
// orders.proto
syntax = "proto3";
package orders;
option php_namespace = "App\\Proto\\Orders";
service OrderService {
rpc GetOrder (GetOrderRequest) returns (Order);
rpc ListOrders (ListOrdersRequest) returns (ListOrdersResponse);
rpc CreateOrder (CreateOrderRequest) returns (Order);
rpc UpdateOrderStatus (UpdateStatusRequest) returns (Order);
// Server streaming — server sends multiple responses
rpc WatchOrder (GetOrderRequest) returns (stream OrderUpdate);
// Client streaming — client sends multiple requests
rpc BatchCreateOrders (stream CreateOrderRequest) returns (BatchResult);
}
message Order {
int64 id = 1;
string client_name = 2;
double total_value = 3;
OrderStatus status = 4;
repeated OrderItem items = 5;
int64 created_at = 6;
}
enum OrderStatus {
PENDING = 0;
PROCESSING = 1;
COMPLETED = 2;
CANCELLED = 3;
}
message OrderItem {
int64 product_id = 1;
string product_name = 2;
int32 quantity = 3;
double unit_price = 4;
}
message GetOrderRequest {
int64 order_id = 1;
}
message ListOrdersRequest {
int32 page = 1;
int32 per_page = 2;
OrderStatus status_filter = 3;
}
message ListOrdersResponse {
repeated Order orders = 1;
int32 total = 2;
}
From this single file, tools generate:
- PHP server implementation stubs
- PHP client code
- JavaScript/TypeScript client
- Go, Python, Java, Rust clients — any language with protobuf support
Implementing gRPC in PHP/Laravel
composer require grpc/grpc google/protobuf
# Generate PHP code from .proto file
protoc --php_out=app/Proto --grpc_out=app/Proto \
--plugin=protoc-gen-grpc=`which grpc_php_plugin` \
proto/orders.proto
<?php
// app/Services/OrderGrpcService.php
namespace App\Services;
use App\Models\Order;
use App\Proto\Orders\Order as ProtoOrder;
use App\Proto\Orders\ListOrdersResponse;
use App\Proto\Orders\OrderServiceInterface;
use Grpc\ServerContext;
class OrderGrpcService implements OrderServiceInterface
{
public function GetOrder(
\App\Proto\Orders\GetOrderRequest $request,
ServerContext $context
): ProtoOrder {
$order = Order::with(['client', 'items'])
->findOrFail($request->getOrderId());
return $this->mapToProto($order);
}
public function ListOrders(
\App\Proto\Orders\ListOrdersRequest $request,
ServerContext $context
): ListOrdersResponse {
$orders = Order::query()
->when($request->getStatusFilter(), fn($q, $status) =>
$q->where('status', $status)
)
->paginate($request->getPerPage(), ['*'], 'page', $request->getPage());
$response = new ListOrdersResponse();
$response->setTotal($orders->total());
$protoOrders = $orders->map(fn($o) => $this->mapToProto($o));
$response->setOrders($protoOrders->toArray());
return $response;
}
private function mapToProto(Order $order): ProtoOrder
{
$proto = new ProtoOrder();
$proto->setId($order->id);
$proto->setClientName($order->client->name);
$proto->setTotalValue($order->total_value);
$proto->setStatus($order->status->value);
$proto->setCreatedAt($order->created_at->timestamp);
return $proto;
}
}
The Four Streaming Modes
This is where gRPC genuinely has no REST equivalent:
service DataService {
// 1. Unary — one request, one response (same as REST)
rpc GetData (Request) returns (Response);
// 2. Server streaming — one request, stream of responses
// Perfect for: live order tracking, log streaming, notifications
rpc StreamUpdates (Request) returns (stream Update);
// 3. Client streaming — stream of requests, one response
// Perfect for: batch uploads, progressive file uploads
rpc BatchUpload (stream DataChunk) returns (UploadResult);
// 4. Bidirectional streaming — both sides stream simultaneously
// Perfect for: real-time chat, live collaboration, trading feeds
rpc LiveSession (stream ClientMessage) returns (stream ServerMessage);
}
gRPC-Web: The Browser Problem
gRPC’s biggest limitation: browsers can’t use it directly. HTTP/2 in browsers doesn’t expose the low-level features gRPC needs.
The solution: gRPC-Web, a proxy layer (typically Envoy or a Go proxy) that sits between the browser and your gRPC server:
Browser → gRPC-Web proxy → gRPC server
(HTTP/1.1) (translates) (HTTP/2)
This adds infrastructure complexity. For public-facing APIs consumed by browsers, REST is still simpler. For internal service-to-service communication where both ends are servers, gRPC is excellent.
When to Choose REST
- Public APIs consumed by external developers — REST + OpenAPI is the standard expectation
- APIs consumed directly by browsers without a backend-for-frontend layer
- Simple CRUD applications with modest traffic
- Small teams where the overhead of protobuf tooling isn’t justified
- When human readability of requests/responses matters for debugging
- Laravel APIs consumed by React/Vue frontends directly
When to Choose gRPC
- Internal microservice communication — service A calling service B in your backend
- High-throughput APIs where JSON serialization overhead is measurable
- Real-time streaming — tracking updates, live dashboards, notification streams
- Polyglot environments where multiple languages need to call the same service
- Mobile backends where payload size impacts battery and data usage
- When strict contract enforcement prevents integration bugs between teams
The Hybrid Architecture: REST + gRPC
The most practical pattern for growing systems — REST externally, gRPC internally:
Client (Browser/Mobile)
↓ REST/JSON
API Gateway / BFF
↓ gRPC ↓ gRPC ↓ gRPC
Order Svc User Svc Payment Svc
↓ gRPC
Inventory Svc
External consumers get a clean REST API they already know how to use. Internal services communicate via gRPC for performance and type safety. The API Gateway handles translation.
This is how companies like Grab, Swiggy, and Zomato structure their backend as they scale. The public API is REST. The 50 internal services talk gRPC.
GraphQL — The Third Option You Should Know About
Since we’re comparing API styles: GraphQL sits between REST and gRPC:
- Like REST: human-readable JSON, browser-native, flexible queries
- Like gRPC: schema-defined, type-safe, prevents over/under fetching
- Unlike both: client controls exactly what data it receives
GraphQL is excellent when: multiple clients (mobile, web, partner integrations) need different shapes of the same data, and you want to avoid building separate endpoints for each.
Bottom line for most Indian projects: Start with REST. Move specific internal services to gRPC when performance becomes a measured constraint. Consider GraphQL only when multiple clients with different data needs become a significant maintenance burden.
If you’re designing a new API architecture and want to make the right call from the start, our backend team at Softcrony is happy to help.
Leave a comment