dotnet-cqrs/docs/getting-started/06-choosing-http-or-grpc.md

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

  1. Build with HTTP (easy to test, debug)
  2. Add gRPC later if needed
  3. Same handlers work for both

Start with gRPC

  1. Build with gRPC (for performance)
  2. Add HTTP later for public API
  3. 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

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