REST has been the default choice for APIs for over a decade. gRPC offers better performance but comes with tradeoffs.
The right choice depends on your clients, your team, and your constraints. This article gives you a framework for deciding.
Common questions this answers
- When should I use gRPC instead of REST?
- How much faster is gRPC than REST?
- Can browsers call gRPC services?
- How do I implement streaming with gRPC?
- Can I use both gRPC and REST in the same application?
Definition (what this means in practice)
gRPC is a high-performance Remote Procedure Call framework that uses Protocol Buffers for serialization and HTTP/2 for transport. REST is an architectural style for APIs that typically uses JSON over HTTP.
In practice, gRPC excels for service-to-service communication where performance matters, while REST remains the standard for browser-accessible public APIs.
Terms used
- Protocol Buffers (protobuf): a binary serialization format that is smaller and faster than JSON.
- HTTP/2: a protocol that supports multiplexing, streaming, and binary framing.
- Streaming: sending multiple messages over a single connection (server streaming, client streaming, or bidirectional).
- gRPC-Web: a browser-compatible variant of gRPC that works over HTTP/1.1.
- Contract-first: defining the API schema before implementation (using .proto files).
Reader contract
This article is for:
- Architects choosing between gRPC and REST for new services.
- Teams evaluating gRPC for microservices communication.
- Developers implementing high-performance APIs.
You will leave with:
- A scoring rubric for choosing between gRPC and REST.
- Implementation patterns for both approaches.
- Understanding of when hybrid approaches make sense.
This is not for:
- GraphQL comparisons (different problem space).
- Message queue or event-driven architecture decisions.
Quick start (10 minutes)
If you do nothing else, understand this decision tree:
Use REST when:
- Browsers need direct access (public APIs)
- Human readability matters (debugging, documentation)
- Team is unfamiliar with gRPC
- Simple CRUD operations with standard HTTP semantics
Use gRPC when:
- Service-to-service communication (microservices)
- Low latency and high throughput required
- Streaming data (real-time updates, large file transfers)
- Bandwidth is constrained (mobile, IoT)
- Multiple languages need to communicate
Feature comparison
| Feature | gRPC | REST with JSON |
|---|---|---|
| Contract | Required (.proto) | Optional (OpenAPI) |
| Protocol | HTTP/2 | HTTP/1.1 or HTTP/2 |
| Payload format | Protobuf (binary) | JSON (text) |
| Message size | Smaller | Larger |
| Browser support | Requires gRPC-Web | Native |
| Streaming | Client, server, bidirectional | Limited |
| Code generation | Built-in | Third-party tools |
| Human readable | No | Yes |
| Tooling maturity | Growing | Mature |
Performance comparison
Message size
Protobuf messages are often smaller than equivalent JSON. Depending on the message shape, Microsoft notes gRPC payloads can be 60-80% smaller than JSON.
// JSON (89 bytes)
{"userId":12345,"name":"John Doe","email":"john@example.com","active":true}
// Protobuf (approximately 45-50 bytes)
// Binary representation of the same data
For high-volume APIs, this difference compounds significantly.
Serialization speed
Protobuf serialization is faster than JSON because:
- Binary format requires no parsing of text
- Schema is known at compile time
- No reflection needed for strongly-typed languages
Depending on message complexity, Microsoft notes gRPC can be up to 8x faster than JSON serialization.
HTTP/2 benefits
gRPC requires HTTP/2, which provides:
- Multiplexing: multiple requests share one TCP connection
- Header compression: reduces overhead for repeated headers
- Binary framing: more efficient than text-based HTTP/1.1
REST APIs can also use HTTP/2, but gRPC is designed around its features.
When performance matters
The performance difference is most significant when:
- Message volume is high (thousands of requests per second)
- Payload sizes are large
- Network bandwidth is constrained
- Latency requirements are strict (sub-millisecond)
For typical web applications with moderate traffic, the performance difference may not justify the added complexity.
Streaming patterns
gRPC supports four communication patterns:
Unary (request-response)
Same as REST. Client sends one message, server responds with one message:
service OrderService {
rpc GetOrder(GetOrderRequest) returns (Order);
}
Server streaming
Client sends one request, server sends multiple responses:
service OrderService {
rpc GetOrderUpdates(GetOrderRequest) returns (stream OrderUpdate);
}
Use case: real-time price feeds, log streaming, progress updates.
// Server implementation
public override async Task GetOrderUpdates(
GetOrderRequest request,
IServerStreamWriter<OrderUpdate> responseStream,
ServerCallContext context)
{
while (!context.CancellationToken.IsCancellationRequested)
{
var update = await GetLatestUpdate(request.OrderId);
await responseStream.WriteAsync(update);
await Task.Delay(1000);
}
}
// Client usage
using var call = client.GetOrderUpdates(new GetOrderRequest { OrderId = 123 });
await foreach (var update in call.ResponseStream.ReadAllAsync())
{
Console.WriteLine($"Order status: {update.Status}");
}
Client streaming
Client sends multiple messages, server responds once:
service UploadService {
rpc UploadFile(stream FileChunk) returns (UploadResult);
}
Use case: file uploads, batch processing, aggregating data.
Bidirectional streaming
Both client and server send multiple messages:
service ChatService {
rpc Chat(stream ChatMessage) returns (stream ChatMessage);
}
Use case: chat applications, collaborative editing, real-time synchronization.
Browser support
Browsers cannot make native gRPC calls because they lack the required HTTP/2 control. Two solutions exist:
gRPC-Web
A browser-compatible protocol that works over HTTP/1.1:
// Program.cs - Enable gRPC-Web
app.UseGrpcWeb();
app.MapGrpcService<GreeterService>().EnableGrpcWeb();
Limitations:
- No client streaming or bidirectional streaming
- Requires gRPC-Web client library in the browser
- Slight overhead compared to native gRPC
gRPC JSON transcoding
Available in .NET 7+, automatically exposes gRPC services as REST APIs:
// Program.cs
builder.Services.AddGrpc().AddJsonTranscoding();
import "google/api/annotations.proto";
service Greeter {
rpc SayHello(HelloRequest) returns (HelloReply) {
option (google.api.http) = {
get: "/v1/greeter/{name}"
};
}
}
Now the same service is accessible via:
- gRPC:
grpc://localhost:5001 - REST:
GET /v1/greeter/World
This is ideal for services that need both internal gRPC efficiency and external REST accessibility.
Error handling differences
gRPC status codes
gRPC uses a fixed set of status codes:
| Code | Meaning |
|---|---|
| OK (0) | Success |
| CANCELLED (1) | Operation cancelled |
| UNKNOWN (2) | Unknown error |
| INVALID_ARGUMENT (3) | Client sent invalid data |
| NOT_FOUND (5) | Resource not found |
| ALREADY_EXISTS (6) | Resource already exists |
| PERMISSION_DENIED (7) | Caller lacks permission |
| UNAUTHENTICATED (16) | No valid credentials |
| DEADLINE_EXCEEDED (4) | Operation timed out |
REST HTTP status codes
REST uses HTTP status codes (200, 400, 404, 500, etc.) with more granularity but less consistency across implementations.
Deadlines and cancellation
gRPC has built-in deadline propagation:
// Client sets deadline
var deadline = DateTime.UtcNow.AddSeconds(5);
var response = await client.GetOrderAsync(
request,
deadline: deadline);
// Server checks deadline
if (context.CancellationToken.IsCancellationRequested)
{
throw new RpcException(new Status(StatusCode.Cancelled, "Deadline exceeded"));
}
REST requires manual timeout handling with HTTP client configuration.
Implementation patterns
gRPC service setup
// Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddGrpc();
var app = builder.Build();
app.MapGrpcService<OrderService>();
app.Run();
// Protos/order.proto
syntax = "proto3";
option csharp_namespace = "MyApp.Grpc";
service OrderService {
rpc GetOrder(GetOrderRequest) returns (Order);
rpc CreateOrder(CreateOrderRequest) returns (Order);
rpc GetOrderUpdates(GetOrderRequest) returns (stream OrderUpdate);
}
message GetOrderRequest {
int32 order_id = 1;
}
message CreateOrderRequest {
string product_id = 1;
int32 quantity = 2;
}
message Order {
int32 id = 1;
string product_id = 2;
int32 quantity = 3;
string status = 4;
}
message OrderUpdate {
int32 order_id = 1;
string status = 2;
string timestamp = 3;
}
gRPC client with factory
// Program.cs - Register client
builder.Services.AddGrpcClient<OrderService.OrderServiceClient>(options =>
{
options.Address = new Uri("https://orders-service:5001");
})
.ConfigurePrimaryHttpMessageHandler(() =>
{
var handler = new SocketsHttpHandler
{
EnableMultipleHttp2Connections = true,
KeepAlivePingDelay = TimeSpan.FromSeconds(60),
KeepAlivePingTimeout = TimeSpan.FromSeconds(30)
};
return handler;
});
// Usage in a service
public class CheckoutService(OrderService.OrderServiceClient orderClient)
{
public async Task<Order> CreateOrderAsync(string productId, int quantity)
{
var request = new CreateOrderRequest
{
ProductId = productId,
Quantity = quantity
};
return await orderClient.CreateOrderAsync(request);
}
}
Decision framework
Score each criterion based on your requirements (1 = not important, 3 = critical):
| Criterion | Favors gRPC | Favors REST | Your Score |
|---|---|---|---|
| Browser clients needed | X | ||
| Service-to-service only | X | ||
| Low latency required | X | ||
| Streaming needed | X | ||
| Team gRPC experience | X | ||
| Human-readable debugging | X | ||
| Existing REST infrastructure | X | ||
| Bandwidth constrained | X | ||
| Public API consumers | X | ||
| Polyglot environment | X |
Scoring:
- gRPC score > REST score: Use gRPC
- REST score > gRPC score: Use REST
- Scores similar: Consider hybrid approach
Common scenarios
Microservices backend: gRPC for internal communication, REST or gRPC-Web for external APIs.
Mobile application: gRPC for efficiency, especially on slow networks.
Public API platform: REST for accessibility, consider gRPC for premium/partner APIs.
Real-time dashboard: gRPC streaming for live updates.
Simple CRUD API: REST, unless specific performance requirements exist.
Hybrid approaches
Many systems use both gRPC and REST:
Pattern 1: Internal gRPC, external REST
Browser/Mobile -> REST API Gateway -> gRPC Microservices
The API gateway handles REST-to-gRPC translation.
Pattern 2: gRPC JSON transcoding
Single service exposes both protocols:
builder.Services.AddGrpc().AddJsonTranscoding();
// Service is accessible via both gRPC and REST
app.MapGrpcService<OrderService>();
Pattern 3: Protocol per client type
Internal services -> gRPC
Web browsers -> REST
Mobile apps -> gRPC (native) or REST (web views)
Copy/paste artifact: gRPC service setup
// Program.cs
var builder = WebApplication.CreateBuilder(args);
// Add gRPC services
builder.Services.AddGrpc(options =>
{
options.EnableDetailedErrors = builder.Environment.IsDevelopment();
options.MaxReceiveMessageSize = 16 * 1024 * 1024; // 16 MB
options.MaxSendMessageSize = 16 * 1024 * 1024;
});
// Optional: Add gRPC-Web support for browsers
builder.Services.AddGrpcWeb();
// Optional: Add JSON transcoding for REST compatibility
builder.Services.AddGrpc().AddJsonTranscoding();
var app = builder.Build();
// Enable gRPC-Web
app.UseGrpcWeb();
// Map gRPC services
app.MapGrpcService<OrderService>().EnableGrpcWeb();
app.Run();
<!-- Project file -->
<ItemGroup>
<PackageReference Include="Grpc.AspNetCore" Version="2.*" />
<PackageReference Include="Grpc.AspNetCore.Web" Version="2.*" />
<Protobuf Include="Protos\*.proto" GrpcServices="Server" />
</ItemGroup>
Copy/paste artifact: gRPC vs REST checklist
gRPC vs REST Decision Checklist
1. Client requirements
- [ ] Browser access needed? -> Favor REST or add gRPC-Web
- [ ] Mobile native apps? -> gRPC viable
- [ ] Service-to-service only? -> gRPC preferred
2. Performance requirements
- [ ] Sub-millisecond latency? -> gRPC
- [ ] High message volume? -> gRPC
- [ ] Bandwidth constrained? -> gRPC
- [ ] Moderate traffic, no strict latency? -> Either works
3. Feature requirements
- [ ] Streaming needed? -> gRPC
- [ ] Bidirectional communication? -> gRPC
- [ ] Simple CRUD? -> REST sufficient
4. Team and tooling
- [ ] Team familiar with gRPC? -> gRPC viable
- [ ] Existing REST infrastructure? -> Consider staying REST
- [ ] Need human-readable debugging? -> REST easier
5. API consumers
- [ ] Public third-party developers? -> REST
- [ ] Internal teams only? -> gRPC viable
- [ ] Multiple languages? -> gRPC (good codegen)
Common failure modes
- Using gRPC for public APIs without fallback: external developers expect REST. Provide gRPC-Web or JSON transcoding.
- Creating new HTTP/2 connections per request: reuse channels. Use gRPC client factory.
- Ignoring deadlines: gRPC has built-in deadline support. Use it to prevent hung requests.
- Large messages without streaming: for payloads over a few MB, use streaming to avoid memory pressure.
- Blocking on async calls: always use async/await with gRPC. Blocking causes thread pool starvation.
Checklist
- Client types identified (browser, mobile, service).
- Performance requirements quantified.
- Streaming needs assessed.
- Team gRPC experience evaluated.
- Hybrid approach considered if requirements mixed.
- gRPC-Web or JSON transcoding planned for browser access.
FAQ
Is gRPC always faster than REST?
For equivalent operations, gRPC is typically faster due to binary serialization and HTTP/2. However, the difference may be negligible for simple, low-volume APIs. Measure your specific use case.
Can I migrate from REST to gRPC incrementally?
Yes. Use gRPC JSON transcoding to expose both protocols from the same service. Migrate clients gradually while maintaining REST compatibility.
Do I need to learn Protocol Buffers?
Yes, but the basics are straightforward. Proto files define messages and services. Code generation handles the rest. Most developers learn enough in a few hours.
What about versioning?
Both support versioning. gRPC uses package names and field numbers (adding fields is backward compatible). REST uses URL versioning or headers. Neither is inherently better.
Should I use gRPC for CRUD APIs?
For simple CRUD with browser clients, REST is usually simpler. For high-performance CRUD between services, gRPC offers benefits. Evaluate based on your specific requirements.
How do I debug gRPC calls?
Use gRPC server reflection and tools like grpcurl. Enable detailed errors in development. For production, use structured logging and distributed tracing.
What to do next
Identify your highest-volume service-to-service communication. Evaluate whether gRPC would provide meaningful performance benefits. If browser access is required, plan for gRPC-Web or JSON transcoding from the start.
For more on building resilient service communication, read Resilience Patterns with Polly: Circuit Breakers, Retries, and Timeouts.
If you want help evaluating gRPC for your architecture, reach out via Contact.
References
- gRPC on .NET
- gRPC benefits (performance and payload size)
- Compare gRPC services with HTTP APIs
- gRPC-Web in ASP.NET Core
- gRPC client factory integration
- Performance best practices for gRPC
- gRPC official documentation
Author notes
Decisions:
- Present as decision framework, not recommendation. Rationale: the right choice depends heavily on context.
- Include hybrid approaches prominently. Rationale: most real systems use both protocols.
- Focus on .NET implementation patterns. Rationale: audience is ASP.NET Core developers.
Observations:
- Teams often choose REST by default without evaluating gRPC for internal services.
- gRPC adoption increases significantly in microservices architectures.
- Browser support is the most common blocker for gRPC adoption.
- JSON transcoding in .NET 7+ makes hybrid approaches much simpler.