Zum Inhalt springen

gRPC vs REST: Wann was einsetzen

Praxisnaher Vergleich von gRPC und REST für Backend-Services: Performance, Developer Experience, Streaming und die entscheidenden Abwägungen.

4 Min. Lesezeit
Gegenüberstellung der Kommunikationsmuster von gRPC und REST zwischen Services als Diagramm

REST ist der Standard-API-Stil für Webservices. gRPC ist der Standard für performante Kommunikation zwischen Services. Die Wahl ist aber nicht immer offensichtlich — viele Teams greifen zu REST, wo gRPC ihnen echten Entwicklungsaufwand ersparen würde, und andere setzen gRPC ein, wo REST vollkommen ausgereicht hätte.

Die Antwort hängt davon ab, wer deine Clients sind, welche Performance du brauchst, ob du Streaming benötigst und wie hoch du Browser-Kompatibilität gegenüber Typsicherheit gewichtest.

Der grundlegende Unterschied

REST nutzt HTTP/1.1 (oder HTTP/2), verschickt in der Regel JSON und leitet seine Semantik aus URL-Pfaden und HTTP-Verben ab. gRPC nutzt HTTP/2, verschickt binäre Protocol-Buffer-Payloads und definiert Services und Methoden in .proto-Dateien, aus denen typisierter Client- und Servercode generiert wird.

tstypescript
// REST: JSON over HTTP — human-readable, widely supported
// POST /api/users
// Content-Type: application/json
// { "name": "Alice", "email": "alice@example.com" }
 
// Response: 201 Created
// { "id": "user-123", "name": "Alice", "email": "alice@example.com" }
 
// gRPC: Protocol Buffers over HTTP/2 — binary, type-safe, fast
// Defined in a .proto file:
protobufprotobuf
// user_service.proto
syntax = "proto3";
 
package users;
 
service UserService {
  rpc CreateUser(CreateUserRequest) returns (CreateUserResponse);
  rpc GetUser(GetUserRequest) returns (User);
  rpc ListUsers(ListUsersRequest) returns (stream User);  // Server streaming
  rpc UpdateUsers(stream UpdateUserRequest) returns (UpdateUsersResponse); // Client streaming
}
 
message CreateUserRequest {
  string name = 1;
  string email = 2;
}
 
message CreateUserResponse {
  string id = 1;
  string name = 2;
  string email = 3;
}
 
message User {
  string id = 1;
  string name = 2;
  string email = 3;
  int64 created_at = 4;  // Unix timestamp
}
 
message GetUserRequest {
  string id = 1;
}
 
message ListUsersRequest {
  int32 page_size = 1;
  string page_token = 2;
}
 
message UpdateUserRequest {
  string id = 1;
  string name = 2;
  string email = 3;
}
 
message UpdateUsersResponse {
  int32 updated_count = 1;
}

Performance im Vergleich

Die binäre Serialisierung von gRPC und das Multiplexing von HTTP/2 verschaffen ihm bei der Kommunikation zwischen Services einen deutlichen Performance-Vorsprung.

tstypescript
// JSON serialization vs Protocol Buffers
 
// A typical user object in JSON: ~150 bytes
const jsonPayload = {
  id: "user-123",
  name: "Alice Johnson",
  email: "alice@example.com",
  createdAt: 1648684800,
  roles: ["admin", "editor"],
};
// JSON.stringify: ~120 bytes
 
// Same data in Protocol Buffers: ~45 bytes
// Binary encoding: field numbers + wire types + values
// No field names transmitted — just indices
// 60-70% smaller on average for typical payloads
 
// Performance implications at scale:
// 10,000 requests/second × 150 bytes (JSON) = 1.5 MB/s network
// 10,000 requests/second × 45 bytes (Protobuf) = 0.45 MB/s network
// 3x less bandwidth, 2-5x faster serialization/deserialization
tstypescript
// ❌ Using REST/JSON for high-throughput internal services
interface InternalServiceCall {
  protocol: 'REST/JSON';
  serialization: '~5ms per large payload';
  networkOverhead: 'HTTP/1.1 head-of-line blocking, text headers';
  typesSafety: 'Manual — OpenAPI spec may drift from implementation';
  // At 50k req/s between services, this adds up significantly
}
 
// ✅ Using gRPC for high-throughput internal services
interface OptimizedServiceCall {
  protocol: 'gRPC/Protobuf';
  serialization: '~0.5ms per large payload';
  networkOverhead: 'HTTP/2 multiplexing, binary headers';
  typeSafety: 'Generated code — proto file IS the implementation contract';
  // 10x serialization improvement, multiplexed connections
}

Streaming: wo gRPC glänzt

gRPC unterstützt vier Kommunikationsmuster nativ. REST unterstützt Request-Response. Alles andere erfordert Behelfslösungen wie WebSockets, Server-Sent Events oder Long Polling.

tstypescript
// gRPC streaming patterns
 
// 1. Unary — standard request/response (like REST)
// Client sends one message, server sends one response
async function getUser(id: string): Promise<User> {
  return client.getUser({ id });
}
 
// 2. Server streaming — server sends a stream of responses
// Client sends one request, server sends many responses
async function* listUsers(pageSize: number): AsyncGenerator<User> {
  const stream = client.listUsers({ pageSize, pageToken: '' });
  for await (const user of stream) {
    yield user;
  }
}
 
// 3. Client streaming — client sends a stream of requests
// Client sends many messages, server sends one response
async function batchUpdateUsers(
  updates: AsyncIterable<UpdateUserRequest>
): Promise<UpdateUsersResponse> {
  return client.updateUsers(updates);
}
 
// 4. Bidirectional streaming — both sides stream simultaneously
// Client and server send messages independently
async function syncUsers(
  localChanges: AsyncIterable<UserChange>,
  onRemoteChange: (change: UserChange) => void
): Promise<void> {
  const stream = client.syncUsers();
 
  // Send local changes
  for await (const change of localChanges) {
    stream.write(change);
  }
 
  // Receive remote changes
  stream.on('data', onRemoteChange);
}

Abwägungen bei der Developer Experience

tstypescript
// REST developer experience
 
// Pros:
// - cURL-friendly: curl http://localhost:3000/api/users/123
// - Browser-testable: just open the URL
// - Massive ecosystem: Express, Fastify, every HTTP library
// - Human-readable payloads: JSON is text
// - Low barrier to entry: everyone knows HTTP + JSON
 
// Cons:
// - No generated types: you write clients manually
// - Schema drift: OpenAPI spec and code get out of sync
// - Verbose: HTTP headers, JSON keys repeated in every message
// - No native streaming: need WebSockets or SSE for real-time
 
// gRPC developer experience
 
// Pros:
// - Generated clients: proto file → TypeScript/Go/Python/etc.
// - Type safety: compiler catches mismatches between services
// - Built-in streaming: first-class support for all 4 patterns
// - Performance: binary serialization, HTTP/2 multiplexing
 
// Cons:
// - Not browser-native: need gRPC-Web proxy or Connect
// - Harder to debug: binary payloads aren't human-readable
// - Tooling complexity: protoc compiler, code generation pipeline
// - Smaller ecosystem: fewer middleware options than REST
tstypescript
// REST: easy to debug with standard tools
// $ curl -s http://localhost:3000/api/users/123 | jq
// {
//   "id": "user-123",
//   "name": "Alice",
//   "email": "alice@example.com"
// }
 
// gRPC: need specialized tools
// $ grpcurl -plaintext localhost:50051 users.UserService/GetUser
// Requires grpcurl, grpc_cli, or Postman gRPC support
// Binary payload must be decoded to be readable

Entscheidungsrahmen

tstypescript
// When to choose REST
const chooseREST = {
  publicAPI: true,               // Clients are external developers
  browserClients: true,          // Frontend applications consume the API
  simpleCRUD: true,              // Standard create/read/update/delete
  teamFamiliarity: 'REST',      // Team knows REST, not gRPC
  debuggability: 'important',    // Need to curl and inspect easily
  ecosystem: 'broad',           // Need extensive middleware options
};
 
// When to choose gRPC
const chooseGRPC = {
  serviceToService: true,        // Internal communication between services
  highThroughput: true,          // 10k+ requests/second between services
  streaming: true,               // Real-time data feeds, event streams
  polyglot: true,                // Services in different languages need type safety
  latencySensitive: true,        // Every millisecond matters
  strictContracts: true,         // Schema-first development with enforcement
};
 
// The hybrid approach: REST for external, gRPC for internal
const hybridArchitecture = {
  externalAPI: 'REST/JSON',      // Public-facing, browser-friendly
  apiGateway: 'translates',      // Converts REST ↔ gRPC at the boundary
  internalServices: 'gRPC',      // Type-safe, high-performance
  // Best of both worlds — but adds gateway complexity
};

Die wichtigsten Punkte

  1. REST für externe APIs, gRPC für interne Services — das gängigste und praktikabelste Muster für moderne Architekturen
  2. Die Typsicherheit von gRPC verhindert Schema-Drift — die .proto-Datei ist zugleich Dokumentation und Quelle der Codegenerierung
  3. Binäre Serialisierung macht sich bei Scale bemerkbar — Protocol Buffers sind 3- bis 10-mal kleiner und lassen sich 2- bis 5-mal schneller serialisieren als JSON
  4. Streaming ist das Killer-Feature von gRPC — Server-Streaming, Client-Streaming und bidirektionales Streaming sind erstklassig unterstützt
  5. REST ist leichter zu debuggen und einzuführen — cURL, die Browser-DevTools und die allgemeine Vertrautheit sorgen für weniger Reibung in den meisten Teams
  6. Zieh einen hybriden Ansatz in Betracht — ein REST-Gateway für externe Clients und gRPC zwischen den internen Services bietet die besten Kompromisse
Wilfredo Rujel

Wilfredo Rujel

Full-Stack-Softwareentwickler

Diesen Beitrag teilenX