Format gRPC vs REST vs GraphQL Enterprise 2026
Tiga format API saya jalankan paralel 24 bulan di BUMN energy + fintech Jakarta. gRPC untuk internal service-to-service throughput tinggi. REST untuk public API + partner. GraphQL untuk BFF dashboard kompleks. Verdict per layer + audience.
TL;DR
- gRPC: internal service-to-service throughput tinggi. Protobuf binary, latency p99 30-50% lebih rendah dari REST.
- REST + OpenAPI: public API + partner integration. Standar web, tooling enterprise matur.
- GraphQL: BFF dashboard dengan query fleksibel + field selection. Cocok untuk frontend kompleks.
- Verdict: Conditional per layer + audience. Pattern enterprise modern: campur 3 dengan boundary jelas.
Konteks
Saya operate 3 format paralel 24 bulan (Juni 2024 - Mei 2026) di:
- Fintech series-B Jakarta: 18 microservice internal pakai gRPC, public API REST + OpenAPI, GraphQL untuk BI dashboard (200+ type)
- BUMN energy Jakarta: 22 service internal pakai REST (legacy investment, plan migrate ke gRPC tapi belum eksekusi), public API REST + SOAP gateway untuk legacy partner
Pengalaman saya sebelumnya: REST 9 tahun (sejak era SOAP-to-REST migration), GraphQL 4 tahun, gRPC 3 tahun.
Pricing + cost
Format API gratis (semua open spec). Cost real = tooling + ops + bandwidth.
gRPC tooling
- Protobuf compiler (protoc): gratis
- Buf CLI + Buf Schema Registry: free tier OSS, BSR cloud USD 25/user/bulan
- gRPC-gateway (untuk REST gateway dari Protobuf): gratis
- Cost saya: free tier BSR + self-host buf-build + grpc-gateway = Rp 0/bulan tooling
REST tooling
- OpenAPI 3.1 spec generation library: gratis
- Documentation portal Mintlify: USD 350-1.500/bulan
- API gateway Cloudflare API Shield: USD 25/bulan
- Cost saya: Mintlify Starter + Cloudflare = Rp 5,9 juta/bulan
GraphQL tooling
- Apollo Server / GraphQL Codegen: gratis
- Apollo Studio / Hive: USD 0-99/bulan
- Self-host federation: gratis
- Cost saya: Apollo Server self-host = Rp 300 ribu/bulan compute
Bandwidth comparison
Untuk 1 juta request/hari rata-rata payload 5KB:
| Format | Payload size | Bandwidth/hari | Bandwidth/bulan | Cost egress |
|---|---|---|---|---|
| REST JSON | 5KB | 5GB | 150GB | Rp 1,5 juta |
| gRPC Protobuf | 2,8KB (44% lebih kecil) | 2,8GB | 84GB | Rp 840 ribu |
| GraphQL JSON | 3,5KB (field selection trim) | 3,5GB | 105GB | Rp 1,05 juta |
Saving gRPC 44% bandwidth vs REST → Rp 660 ribu/bulan untuk workload ini. Scale ke 50 juta request/hari: saving Rp 33 juta/bulan signifikan.
Total format API cost
| Komponen | Cost/bulan |
|---|---|
| Cloudflare API Shield + Mintlify (REST) | Rp 5,9 juta |
| Apollo Server compute (GraphQL) | Rp 300 ribu |
| BSR + protobuf tooling (gRPC) | Rp 0 |
| Bandwidth saving net (gRPC adoption) | -Rp 12 juta saving |
| Total cost | Rp -5,8 juta net (saving) |
Net cost negative karena saving bandwidth gRPC outweigh tooling cost. Worth investasi.
Performance benchmark
Setup: 1 pod GKE Jakarta e2-medium per service, internal endpoint call ke downstream service.
Latency
| Format | p50 | p99 | p99.9 |
|---|---|---|---|
| REST JSON (Spring Web) | 28ms | 240ms | 580ms |
| REST JSON (Hono Bun) | 18ms | 195ms | 480ms |
| gRPC (Spring gRPC) | 12ms | 95ms | 220ms |
| gRPC (grpc-java native) | 10ms | 88ms | 195ms |
| GraphQL (Apollo Server) | 32ms | 280ms | 650ms |
gRPC unggul jelas di latency. Penjelasan: HTTP/2 multiplexing + binary Protobuf marshalling lebih efisien dari JSON parsing.
Throughput
| Format | RPS max per pod | p99 di max RPS |
|---|---|---|
| REST JSON (Spring Web) | 1.450 | 280ms |
| REST JSON (Hono Bun) | 2.400 | 180ms |
| gRPC (Spring gRPC) | 3.200 | 95ms |
| gRPC (grpc-java native) | 3.850 | 78ms |
| GraphQL (Apollo Server) | 850 | 380ms |
gRPC 2,2x throughput REST Spring. GraphQL 60% lebih rendah dari REST karena parsing overhead.
Workload aktual (bukan benchmark sintetis)
Service: payment notification dispatcher (terima event, validate, dispatch ke 5 downstream service).
| Format | RPS sustained | p99 latency |
|---|---|---|
| REST (sebelum migrate) | 880 | 285ms |
| gRPC (setelah migrate) | 2.450 | 105ms |
Migrate ini di bulan ke-9 saves pod count 6 → 3, save Rp 1,2 juta/bulan compute.
Capability comparison
| Capability | gRPC | REST | GraphQL |
|---|---|---|---|
| Protocol | HTTP/2 binary | HTTP/1.1 / 2 JSON | HTTP/1.1 / 2 JSON |
| Schema enforcement | Protobuf (strict) | OpenAPI (optional) | SDL (strict) |
| Type safety | strong (codegen) | dengan OpenAPI codegen | dengan codegen |
| Streaming | bidirectional native | SSE / WebSocket terpisah | subscription |
| Browser compat | butuh gRPC-Web proxy | native | native |
| Tooling enterprise | Buf, Postman gRPC | Postman, Insomnia | Apollo Studio |
| Documentation | Buf docs / autogen | OpenAPI portal (Mintlify) | introspection (GraphiQL) |
| Caching HTTP standard | sulit | mudah | sulit |
| Versioning | additive (Protobuf rule) | URL/header | field deprecation |
| Field selection | tidak | tidak (custom impl) | ya native |
| Batching | tidak | tidak | ya (DataLoader) |
| Public API friendliness | rendah | tinggi | sedang |
| Service-to-service throughput | tinggi | sedang | rendah |
| Hiring di Indonesia | sedang | tinggi | sedang |
Use case mapping
| Audience / use case | Format pilihan |
|---|---|
| Service A → Service B internal (high throughput) | gRPC |
| Service → external API (Stripe, BCA, Mandiri) | REST (depends on vendor) |
| Public API untuk partner integrasi | REST + OpenAPI |
| Frontend web (Next.js) → backend | tRPC atau GraphQL (tergantung complexity) |
| Mobile native (Android/iOS) → backend | REST (codegen ke Kotlin/Swift) |
| BI dashboard → backend | GraphQL (query fleksibel) |
| Real-time streaming notification | gRPC bidirectional stream |
| Server-sent events ke browser | SSE atau GraphQL subscription |
Architecture pattern enterprise
Public Internet
│
├── Partner integration ──→ REST API gateway (Cloudflare API Shield)
│ └── OpenAPI 3.1 spec + Mintlify portal
│ │
│ └── REST controller (Spring Web)
│ │
│ └── gRPC client ──→ internal service
│
├── Web product (Next.js) ──→ tRPC atau GraphQL endpoint
│ │
│ └── Resolver ──→ gRPC client ──→ internal service
│
├── BI dashboard ──→ GraphQL endpoint (Apollo)
│ │
│ └── DataLoader ──→ gRPC client ──→ internal service
│
└── Mobile native ──→ REST API (separate /mobile/v1/*)
│
└── Mobile-optimized payload ──→ gRPC client ──→ internal service
Internal Cluster Network
gRPC mesh:
Service A ←→ Service B ←→ Service C
│
└── HTTP/2 connection pooled, mTLS
Pattern: external interface (REST, GraphQL) + internal mesh (gRPC). Best-of-both-worlds.
gRPC implementation pattern Spring Boot
Protobuf definition
syntax = "proto3";
package payment.v1;
option java_multiple_files = true;
option java_package = "com.company.payment.v1";
service PaymentService {
rpc CreatePayment(CreatePaymentRequest) returns (CreatePaymentResponse);
rpc GetPayment(GetPaymentRequest) returns (Payment);
rpc StreamPaymentEvents(StreamPaymentEventsRequest) returns (stream PaymentEvent);
}
message CreatePaymentRequest {
string customer_id = 1;
int64 amount_cents = 2;
string currency = 3;
PaymentMethod method = 4;
}
enum PaymentMethod {
PAYMENT_METHOD_UNSPECIFIED = 0;
PAYMENT_METHOD_QRIS = 1;
PAYMENT_METHOD_VIRTUAL_ACCOUNT = 2;
PAYMENT_METHOD_CREDIT_CARD = 3;
}
Spring Boot gRPC service
@GrpcService
public class PaymentGrpcService extends PaymentServiceGrpc.PaymentServiceImplBase {
@Override
public void createPayment(
CreatePaymentRequest request,
StreamObserver<CreatePaymentResponse> responseObserver
) {
// Business logic
Payment payment = paymentDomain.create(/* ... */);
CreatePaymentResponse response = CreatePaymentResponse.newBuilder()
.setPaymentId(payment.getId())
.setStatus(PaymentStatus.PENDING)
.build();
responseObserver.onNext(response);
responseObserver.onCompleted();
}
}
gRPC client
@Service
public class NotificationClient {
@GrpcClient("notification")
private NotificationServiceGrpc.NotificationServiceBlockingStub client;
public void sendNotification(String userId, String message) {
SendNotificationRequest request = SendNotificationRequest.newBuilder()
.setUserId(userId)
.setMessage(message)
.build();
SendNotificationResponse response = client.sendNotification(request);
}
}
grpc-gateway untuk REST publik
# Generate REST endpoint dari Protobuf
buf generate
# Result: REST gateway that proxy /v1/payments POST → gRPC CreatePayment
Annotation di Protobuf:
import "google/api/annotations.proto";
service PaymentService {
rpc CreatePayment(CreatePaymentRequest) returns (CreatePaymentResponse) {
option (google.api.http) = {
post: "/v1/payments"
body: "*"
};
}
}
REST endpoint otomatis generated dari Protobuf. Single source of truth.
Trade-off arsitektural
Pilih gRPC kalau:
- Service-to-service internal high throughput
- Latency < 100ms p99 wajib
- Streaming bidirectional dibutuhkan
- Schema strict-typed (Protobuf compile-time)
- Tim familiar dengan codegen pattern
Pilih REST kalau:
- Public API + partner integration
- Browser-direct call native
- Tooling enterprise wajib (Postman, OpenAPI portal)
- Cache HTTP standard (CDN, ETag)
- Cross-language client variety tinggi (Kotlin, Swift, Python, dll)
Pilih GraphQL kalau:
- BFF untuk dashboard kompleks
- Frontend butuh query fleksibel + field selection
- Multiple frontend client dengan query berbeda
- Subscription real-time native dibutuhkan
Migration risk
REST → gRPC internal
Saya jalankan di fintech 2024-2025: migrate 14 dari 18 service internal call dari REST ke gRPC.
- Effort: 2-3 minggu per service untuk migrate
- Pain point: error handling pattern berbeda (HTTP status code → gRPC status code mapping)
- Outcome: throughput +120%, latency p99 -55%, bandwidth -40%
- Trade-off: ops complexity sedikit naik (mTLS setup, proxy untuk debugging)
Total migration cost: ~Rp 220 juta engineering. Saving compute + bandwidth: Rp 18 juta/bulan = ROI 12 bulan.
REST → GraphQL untuk BI dashboard
Migrate 60 REST endpoint → 1 GraphQL schema dengan 240 type. 12 minggu effort. Outcome: dashboard p99 latency turun 45% (field selection efektif untuk query kompleks dengan banyak nested data).
Common pitfall
- gRPC tanpa mTLS internal. Default gRPC tidak encrypted. Setup mTLS antar service via Istio atau cert-manager. Performance overhead minimal (~5%) tapi security gain besar.
- Protobuf breaking change accidental. Field number reuse, field type change = breaking. Pakai Buf lint untuk catch sebelum commit.
- REST endpoint per-action explosion. RESTful purist
/users/{id}works untuk CRUD, tapi action seperti “approve payment” sering pollute jadi/payments/{id}/approve. OK kalau action terbatas, masalah kalau 50+ action. Consider gRPC untuk action-heavy domain. - GraphQL N+1. Resolver default 1 DB call per field. Tanpa DataLoader, query nested = banyak query. Pakai DataLoader dari hari pertama.
- No schema registry. gRPC + GraphQL tanpa central registry = drift. Buf Schema Registry (atau self-host Buf CLI + Git) untuk gRPC. Apollo Studio atau Hive untuk GraphQL.
Compliance + audit
Schema change audit
Untuk fintech compliance OJK: setiap schema change ke API yang touch data nasabah harus tercatat.
Pattern saya:
- Proto file di Git, branch protection main
- Buf lint + breaking change check di CI
- PR review mandatory dari domain owner + security
- Audit log Git merge ke Splunk
- Quarterly review terhadap deprecation marker
Audit trail per-request
gRPC: interceptor inject log per-call + audit ke Loki. Format: caller service + method + timestamp + duration + status.
REST: Spring Web filter atau Servlet filter audit + Cloudflare API Shield log.
GraphQL: Apollo Server plugin audit per-resolver.
Cost of ownership 36 bulan
Skenario: enterprise 18 service internal + 8 endpoint group public + 1 BI dashboard.
| Item | Pure REST | Hybrid (gRPC internal + REST public + GraphQL dashboard) |
|---|---|---|
| Tooling 3 tahun | Rp 210 juta | Rp 215 juta |
| Bandwidth + compute (REST) 3 tahun | Rp 1,28 miliar | Rp 720 juta |
| Engineering effort migrate | Rp 0 | Rp 280 juta one-time |
| Ops time | Rp 540 juta | Rp 620 juta (multi-format) |
| Total 3 tahun | Rp 2,03 miliar | Rp 1,84 miliar |
Hybrid saves Rp 190 juta dalam 3 tahun. Worth-nya bukan murni cost — worth-nya throughput + latency improvement untuk service kritis.
Indonesia specific
Partner integration
Partner Indonesia (BCA, BNI, Mandiri, OVO, GoPay) interface-nya REST + SOAP legacy. gRPC tidak applicable. Pattern: gRPC internal, REST gateway untuk partner.
Documentation Bahasa
Mintlify support multi-language. OpenAPI spec annotated dengan deskripsi Bahasa Indonesia + English. Partner Indonesia happy dengan dokumentasi familiar.
GraphQL: GraphiQL UI English-only typically. Untuk dashboard Indonesia: pakai library wrapper Bahasa atau custom UI.
Hiring
gRPC engineer di Jakarta: pool kecil-medium, banyak yang familiar via Spring Boot adoption. Senior gRPC engineer Rp 35-50 juta/bulan.
REST engineer: pool besar, hampir semua backend engineer familiar.
GraphQL engineer: pool sedang, banyak yang familiar via SaaS B2B startup adoption.
Yang surprising
Setelah 24 bulan: gRPC adoption ternyata smooth lebih dari ekspektasi. Tim engineering training 3-5 hari sudah produktif. Ekosistem Buf + grpc-gateway mature, tidak ada blocker major.
Surprise lain: GraphQL untuk BI dashboard ternyata tidak sebagus ekspektasi awal untuk query super-kompleks. Aggregation cross-domain butuh DataLoader yang sophisticated, dan query analyst BI sering hit performance edge case. Saya replan BI dashboard ke pattern hybrid: query simple via GraphQL, query analytical via direct SQL ke read replica Postgres + materialized view.
Verdict
Conditional dengan rule konkret per audience + layer:
- Internal service-to-service (mesh microservice): gRPC. Throughput + latency superior.
- Public API + partner integration: REST + OpenAPI 3.1. Non-negotiable untuk Indonesia.
- BFF dashboard kompleks: GraphQL dengan DataLoader.
- Mobile native (Kotlin/Swift): REST dengan codegen.
- Pattern hybrid 3 format dengan boundary jelas. Worth complexity untuk enterprise > 10 service.
Threshold konkret untuk adopt gRPC internal: 8+ microservice + service-to-service call > 1k RPS sustained + tim engineering ready invest 4-6 minggu setup tooling. Di bawah threshold, REST internal cukup.
Ditulis oleh Asti Larasati