10 KiB
Choosing HTTP vs gRPC
Understand when to use HTTP, gRPC, or both protocols for your application.
Quick Decision Guide
| Scenario | Recommendation |
|---|---|
| Public API | ✅ HTTP |
| Web browser clients | ✅ HTTP |
| Mobile apps (REST) | ✅ HTTP |
| Microservices (internal) | ✅ gRPC |
| High-performance APIs | ✅ gRPC |
| Low-latency requirements | ✅ gRPC |
| Need both internal & public | ✅ Both (Dual Protocol) |
| Existing REST clients | ✅ HTTP |
| .NET-to-.NET communication | ✅ gRPC |
HTTP (Minimal API)
What It Is
HTTP integration uses ASP.NET Core Minimal API to expose commands and queries as REST endpoints.
When to Use
✅ Best for:
- Public APIs
- Web browser clients (JavaScript, React, Vue, etc.)
- Mobile apps expecting REST
- Third-party integrations
- Developer-friendly exploration (Swagger)
- Simple authentication (API keys, JWT)
Pros
- ✅ Universal compatibility - Works everywhere (browsers, curl, Postman)
- ✅ Human-readable - JSON payloads are easy to debug
- ✅ Swagger/OpenAPI - Automatic API documentation
- ✅ Caching - HTTP caching headers work out of the box
- ✅ Familiar - Developers know REST
- ✅ Tooling - Excellent debugging and testing tools
Cons
- ❌ Lower performance - Text-based JSON vs binary Protocol Buffers
- ❌ Larger payloads - JSON is verbose compared to binary
- ❌ No streaming - Single request-response (without SSE/WebSockets)
- ❌ Manual client code - No automatic client generation
Example Setup
var builder = WebApplication.CreateBuilder(args);
// Register CQRS
builder.Services.AddSvrntyCQRS();
builder.Services.AddDefaultCommandDiscovery();
builder.Services.AddDefaultQueryDiscovery();
// Register handlers
builder.Services.AddCommand<CreateUserCommand, int, CreateUserCommandHandler>();
builder.Services.AddQuery<GetUserQuery, UserDto, GetUserQueryHandler>();
// Add Swagger (optional)
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Map HTTP endpoints
app.UseSvrntyCqrs();
// Enable Swagger
app.UseSwagger();
app.UseSwaggerUI();
app.Run();
Usage
# Commands (POST only)
curl -X POST http://localhost:5000/api/command/createUser \
-H "Content-Type: application/json" \
-d '{"name":"Alice","email":"alice@example.com"}'
# Queries (GET or POST)
curl "http://localhost:5000/api/query/getUser?userId=1"
curl -X POST http://localhost:5000/api/query/getUser \
-H "Content-Type: application/json" \
-d '{"userId":1}'
gRPC
What It Is
gRPC integration uses Protocol Buffers and HTTP/2 for high-performance, strongly-typed communication.
When to Use
✅ Best for:
- Microservices (service-to-service)
- Internal APIs
- High-performance requirements
- Low-latency communication
- Strongly-typed contracts
- .NET-to-.NET or polyglot services
Pros
- ✅ High performance - Binary protocol, HTTP/2 multiplexing
- ✅ Compact payloads - Protocol Buffers are smaller than JSON
- ✅ Strong typing - Compile-time type safety
- ✅ Code generation - Automatic client generation
- ✅ Streaming - Bidirectional streaming support
- ✅ Rich error model - Structured error details
Cons
- ❌ Browser support limited - Requires gRPC-Web proxy
- ❌ Learning curve - .proto files, Protocol Buffers
- ❌ Less human-readable - Binary format
- ❌ Tooling - Fewer debugging tools than REST
- ❌ Firewall issues - HTTP/2 may be blocked
Example Setup
var builder = WebApplication.CreateBuilder(args);
// Register CQRS
builder.Services.AddSvrntyCQRS();
builder.Services.AddDefaultCommandDiscovery();
builder.Services.AddDefaultQueryDiscovery();
// Register handlers
builder.Services.AddCommand<CreateUserCommand, int, CreateUserCommandHandler>();
builder.Services.AddQuery<GetUserQuery, UserDto, GetUserQueryHandler>();
// Add gRPC
builder.Services.AddGrpc();
var app = builder.Build();
// Map gRPC services (auto-generated by source generators)
app.MapGrpcService<CommandServiceImpl>();
app.MapGrpcService<QueryServiceImpl>();
// Enable gRPC reflection (for tools like grpcurl)
app.MapGrpcReflectionService();
app.Run();
Proto File
Create Protos/cqrs_services.proto:
syntax = "proto3";
option csharp_namespace = "MyApp.Grpc";
service CommandService {
rpc CreateUser (CreateUserRequest) returns (CreateUserResponse);
}
service QueryService {
rpc GetUser (GetUserRequest) returns (GetUserResponse);
}
message CreateUserRequest {
string name = 1;
string email = 2;
}
message CreateUserResponse {
int32 result = 1;
}
message GetUserRequest {
int32 user_id = 1;
}
message GetUserResponse {
int32 id = 1;
string name = 2;
string email = 3;
}
Note: Svrnty.CQRS source generators automatically implement these services for you!
Usage
# Using grpcurl
grpcurl -plaintext \
-d '{"name":"Alice","email":"alice@example.com"}' \
localhost:5000 \
CommandService/CreateUser
grpcurl -plaintext \
-d '{"userId":1}' \
localhost:5000 \
QueryService/GetUser
Dual Protocol (Both HTTP & gRPC)
What It Is
Run both HTTP and gRPC endpoints simultaneously, allowing clients to choose their preferred protocol.
When to Use
✅ Best for:
- Hybrid scenarios (public + internal APIs)
- Gradual migration from REST to gRPC
- Supporting multiple client types
- Maximum flexibility
Example Setup
var builder = WebApplication.CreateBuilder(args);
// Register CQRS
builder.Services.AddSvrntyCQRS();
builder.Services.AddDefaultCommandDiscovery();
builder.Services.AddDefaultQueryDiscovery();
// Register handlers
builder.Services.AddCommand<CreateUserCommand, int, CreateUserCommandHandler>();
builder.Services.AddQuery<GetUserQuery, UserDto, GetUserQueryHandler>();
// Add HTTP support
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
// Add gRPC support
builder.Services.AddGrpc();
var app = builder.Build();
// Map HTTP endpoints
app.UseSvrntyCqrs();
// Map gRPC services
app.MapGrpcService<CommandServiceImpl>();
app.MapGrpcService<QueryServiceImpl>();
app.MapGrpcReflectionService();
// Enable Swagger
app.UseSwagger();
app.UseSwaggerUI();
app.Run();
Benefits
- ✅ Flexibility - Clients choose protocol
- ✅ Same codebase - One implementation, two protocols
- ✅ Gradual migration - Transition clients over time
- ✅ Best of both - Public REST + internal gRPC
Trade-offs
- ❌ Slightly more complexity - More configuration
- ❌ Two sets of clients - Maintain both if not auto-generated
- ❌ Larger dependencies - Both HTTP and gRPC packages
Performance Comparison
Latency
gRPC: ~50% lower latency than HTTP/JSON
gRPC: 1-2 ms
HTTP/JSON: 2-4 ms
Results vary based on payload size and network conditions
Throughput
gRPC: ~2-3x higher throughput
gRPC: 50,000+ requests/sec
HTTP/JSON: 15,000-20,000 requests/sec
On same hardware
Payload Size
gRPC: ~30-50% smaller payloads
Protocol Buffers: 100 bytes
JSON: 200-300 bytes
For typical messages
Feature Comparison
| Feature | HTTP (Minimal API) | gRPC |
|---|---|---|
| Performance | Good | Excellent |
| Browser support | ✅ Yes | ❌ No (requires gRPC-Web) |
| Caching | ✅ Native HTTP caching | ❌ Not built-in |
| Streaming | ❌ No (without WebSockets) | ✅ Yes (bidirectional) |
| Code generation | ❌ No | ✅ Yes |
| Human-readable | ✅ JSON | ❌ Binary |
| Tooling | ✅ Excellent (Swagger, Postman) | ⚠️ Limited (grpcurl, Postman) |
| Learning curve | ✅ Low | ⚠️ Medium |
| Type safety | ⚠️ Runtime | ✅ Compile-time |
Decision Matrix
Choose HTTP If:
- ✅ You need browser/JavaScript clients
- ✅ Public API for third parties
- ✅ You want Swagger documentation
- ✅ Team is familiar with REST
- ✅ Caching is important
- ✅ Human readability matters
Choose gRPC If:
- ✅ Microservices architecture
- ✅ Internal APIs only
- ✅ Performance is critical
- ✅ You need streaming
- ✅ Strong typing is important
- ✅ Polyglot environment (.NET, Go, Java, Python)
Choose Both If:
- ✅ You have both public and internal clients
- ✅ You want flexibility
- ✅ You're migrating from REST to gRPC
- ✅ Different teams have different needs
Migration Path
Start with HTTP
- Build with HTTP (easy to test, debug)
- Add gRPC later if needed
- Same handlers work for both
Start with gRPC
- Build with gRPC (for performance)
- Add HTTP later for public API
- Same handlers work for both
Real-World Examples
E-Commerce Platform
// Public-facing API → HTTP
// Mobile app → HTTP
// Internal order processing → gRPC
// Payment service → gRPC
// Inventory service → gRPC
Recommendation: Dual protocol
Internal Microservices
// All service-to-service → gRPC
// Admin dashboard → HTTP (optional)
Recommendation: gRPC only
SaaS Product
// Customer API → HTTP
// JavaScript SDK → HTTP
// Webhooks → HTTP
Recommendation: HTTP only
Next Steps
Congratulations! You've completed the Getting Started guide. You now know:
- ✅ What CQRS is and when to use it
- ✅ How to install Svrnty.CQRS
- ✅ How to create commands and queries
- ✅ How to add validation
- ✅ When to use HTTP vs gRPC
Continue Learning
- Architecture - Understand the framework design
- Core Features - Deep dive into commands, queries, and dynamic queries
- HTTP Integration - Master HTTP endpoints
- gRPC Integration - Master gRPC services
- Event Streaming - Build event-sourced applications
- Tutorials - Learn through comprehensive examples
Try the Sample Project
Check out the complete sample application:
cd Svrnty.Sample
dotnet run
Visit:
- HTTP:
http://localhost:5000/swagger - gRPC: Use grpcurl or a gRPC client
See Also
- HTTP Integration Overview - Complete HTTP guide
- gRPC Integration Overview - Complete gRPC guide
- Endpoint Mapping - How HTTP endpoints work
- Proto File Setup - How .proto files work