API (Application Programming Interface) adalah kontrak yang mengikat pemanggil dan penyedia layanan, dan gaya API menentukan bentuk kontrak itu. Topik ini membandingkan tiga gaya yang paling banyak dipakai untuk komunikasi antarlayanan, yaitu REST, gRPC, dan GraphQL, lengkap dengan ukuran payload dan jumlah pulang-pergi yang dihitung dari data yang sama. Setelah membacanya, pemilihan gaya untuk API publik, komunikasi internal, dan agregasi data bagi frontend memiliki untung-rugi (tradeoff) yang terukur.
REST: resource sebagai kontrak#
REST (Representational State Transfer) memperlakukan data sebagai resource yang dialamatkan dengan URI dan dimanipulasi melalui metode HTTP. Batasan arsitekturnya dirumuskan dalam disertasi Fielding, dan yang paling berpengaruh bagi desain adalah antarmuka seragam (uniform interface), yaitu kumpulan metode yang sama untuk semua resource sehingga pemanggil baru dapat menebak perilakunya. REST juga menuntut stateless, yaitu setiap permintaan membawa seluruh informasi yang dibutuhkan server, sehingga permintaan apa pun dapat dilayani oleh server mana pun di balik load balancer.
Contoh berikut adalah jawaban JSON untuk daftar catatan pada endpoint GET /catatan.
[
{"id": 1, "judul": "Kalkulator diskon", "dibuat_pada": "2026-10-01", "dilihat": 120},
{"id": 2, "judul": "Penghitung kata", "dibuat_pada": "2026-10-04", "dilihat": 95}
]Kekuatan REST terletak pada kesederhanaan dan ekosistemnya. Semantik cache HTTP bekerja tanpa lapisan tambahan, alat debug cukup berupa curl, dan integrasi pihak ketiga tidak memerlukan pustaka khusus. Harganya: server mengembalikan bentuk yang telah ditetapkan, sehingga pemanggil yang hanya membutuhkan dua kolom tetap menerima seluruh kolom, dan kebutuhan baru memaksa endpoint baru atau perubahan jawaban yang berdampak luas.
gRPC: kontrak yang dikompilasi#
gRPC mendefinisikan kontrak sebagai skema protobuf, yaitu bahasa definisi antarmuka (interface definition language) yang dikompilasi menjadi kode klien dan server. Skema menyatakan layanan, metode, dan tipe data setiap field, sehingga ketidakcocokan antarversi tertangkap pada waktu kompilasi, bukan di production. Transportnya adalah HTTP/2, sehingga banyak permintaan berbagi satu koneksi melalui multiplexing.
syntax = "proto3";
service PencariCatatan {
rpc Cari (KueriCatatan) returns (HasilCatatan);
}
message KueriCatatan {
string kata_kunci = 1;
int32 batas = 2;
}
message HasilCatatan {
repeated string judul = 1;
}gRPC menyediakan empat pola panggilan, yaitu unary, server streaming, client streaming, dan dua arah (bidirectional streaming). Pengodean biner protobuf membuat payload jauh lebih kecil daripada JSON, dan deadline, yaitu batas waktu panggilan, dapat disertakan lalu diteruskan ke layanan berikutnya. Di sisi lain, payload biner tidak terbaca tanpa skemanya, cache HTTP tidak dapat bekerja secara langsung, dan pemanggilan dari browser memerlukan perlakuan khusus, sehingga gaya ini paling cocok untuk komunikasi internal antarlayanan.
GraphQL: bentuk respons ditentukan pemanggil#
GraphQL menyatukan seluruh API pada satu endpoint dan memindahkan penentuan bentuk respons ke sisi klien. Klien mengirim kueri yang menyebut kolom yang dibutuhkan, lalu server merespons data dengan bentuk yang persis mengikuti kuerinya. Skemanya bertipe (typed schema) sehingga kueri dapat divalidasi sebelum dieksekusi.
query {
catatan(batas: 2) {
judul
}
}Pendekatan ini menyelesaikan dua masalah REST pada klien yang beragam, yaitu overfetching yang membuat pemanggil menerima kolom tidak terpakai dan underfetching yang membuatnya kekurangan kolom sehingga harus menambah permintaan. Satu kueri juga dapat menggabungkan data dari beberapa sumber sekaligus melalui resolver, yaitu fungsi yang mengisi setiap kolom pada skema, sehingga frontend tidak perlu menumpuk permintaan ke banyak endpoint. Harganya berada di sisi server, yaitu cache HTTP menjadi sulit karena permintaan berupa POST ke satu endpoint, kueri berlapis-lapis dapat memicu eksekusi berlipat yang dikenal sebagai masalah N+1, dan batasan kedalaman serta kompleksitas kueri wajib ditegakkan.
Menghitung perbedaan ukuran dan latensi#
Perbedaan gaya paling mudah dilihat dari dua angka, yaitu ukuran payload dan jumlah pulang-pergi atau RTT (round-trip time). Skrip berikut menghitung keduanya dari data yang sama, yaitu tiga catatan dengan tiga kolom. Ukuran REST dihitung dari JSON yang mengembalikan seluruh kolom, GraphQL dari JSON yang hanya memuat judul, dan biner bergaya protobuf dari pengodean field bernomor dengan kode varint. Pengodean biner itu adalah ilustrasi sederhana bergaya protobuf, bukan protobuf asli, sedangkan bagian latensi menyimulasikan layar dasbor yang membutuhkan data dari tiga sumber.
# Membandingkan ukuran payload REST, GraphQL, dan pengodean biner bergaya
# protobuf untuk data yang sama, plus jumlah pulang-pergi untuk satu layar.
# Pengodean biner di sini adalah ilustrasi TLV sederhana, bukan protobuf asli.
import json
catatan = [
{"id": 1, "judul": "Kalkulator diskon", "dibuat_pada": "2026-10-01"},
{"id": 2, "judul": "Penghitung kata", "dibuat_pada": "2026-10-04"},
{"id": 3, "judul": "Konversi satuan", "dibuat_pada": "2026-10-09"},
]
# REST: server mengembalikan seluruh kolom yang dimilikinya
rest = json.dumps(catatan, separators=(",", ":")).encode("utf-8")
# GraphQL: client hanya meminta judul sehingga respons lebih ramping
graphql = json.dumps([{"judul": c["judul"]} for c in catatan],
separators=(",", ":")).encode("utf-8")
# Biner bergaya protobuf: tiap field bernomor, panjang memakai kode varint
def varint(n: int) -> bytes:
keluar = bytearray()
while True:
b = n & 0x7F
n >>= 7
if n:
keluar.append(b | 0x80)
else:
keluar.append(b)
return bytes(keluar)
biner = bytearray()
for c in catatan:
pesan = bytearray()
pesan += varint(1 << 3 | 0) + varint(c["id"]) # field 1: int
judul = c["judul"].encode("utf-8")
pesan += varint(2 << 3 | 2) + varint(len(judul)) + judul # field 2: string
biner += varint(1 << 3 | 2) + varint(len(pesan)) + pesan # field 1: berulang
biner = bytes(biner)
print(f"REST, seluruh kolom : {len(rest)} byte")
print(f"GraphQL, judul saja : {len(graphql)} byte")
print(f"Biner bergaya protobuf : {len(biner)} byte")
# Latensi: satu layar dasbor membutuhkan data dari tiga endpoint
RTT = 40 # ms
print(f"REST, tiga endpoint berurutan : {3 * RTT} ms")
print(f"GraphQL, satu endpoint : {RTT} ms")REST, seluruh kolom : 189 byte
GraphQL, judul saja : 87 byte
Biner bergaya protobuf : 65 byte
REST, tiga endpoint berurutan : 120 ms
GraphQL, satu endpoint : 40 msPayload biner hanya 65 byte dibandingkan dengan 189 byte pada REST untuk data yang sama, sedangkan GraphQL yang memangkas kolomnya turun menjadi 87 byte. Selisih ukuran ini berlipat pada trafik besar, tetapi bagian latensi menunjukkan sisi lain, yaitu tiga permintaan REST berurutan membayar 120 ms, sedangkan satu kueri GraphQL cukup 40 ms. Kedua angka dihitung dari asumsi RTT yang sama, sehingga perbandingannya adil selama pola aksesnya juga sama.
Memilih gaya API#
Tabel berikut merangkum tradeoff ketiga gaya pada aspek yang paling sering menjadi penentu.
| Aspek | REST | gRPC | GraphQL |
|---|---|---|---|
| Bentuk kontrak | Dokumentasi OpenAPI yang disiplin | Skema protobuf yang dikompilasi | Skema GraphQL yang bertipe |
| Format pesan | JSON berbasis teks | Biner protobuf | JSON berbasis teks |
| Transport | HTTP/1.1 atau HTTP/2 | HTTP/2 | HTTP dengan satu endpoint |
| Caching HTTP | Langsung memakai semantik HTTP | Tidak langsung | Sulit, respons POST jarang ter-cache |
| Pola streaming | Terbatas | Empat pola termasuk dua arah | Melalui subscription terpisah |
| Cocok untuk | API publik dan integrasi luas | Komunikasi internal antarlayanan | Agregasi data untuk frontend |
Ketiga gaya dapat hidup berdampingan dalam satu arsitektur, dan pemilihannya mengikuti khalayak masing-masing. REST menjadi pilihan aman untuk API publik karena perangkat pendukungnya (tooling) universal dan caching-nya bekerja apa adanya. gRPC lebih unggul ketika latensi dan streaming menjadi sasaran utama pada komunikasi internal, sedangkan GraphQL lebih unggul ketika banyak frontend dengan kebutuhan data berbeda menumpuk di satu gerbang. Menyatukan keduanya, misalnya REST di tepi dan gRPC di dalam, adalah pola yang lazim dan bukan tanda desain yang terpecah.
Kesalahan umum#
Tiga kesalahan berikut muncul ketika kontrak API dibentuk tanpa mengikuti gaya yang dipilih.
- Merancang REST dengan endpoint berupa aksi, misalnya
/buatCatatandan/hapusCatatan. Kontrak REST hidup di resource dan metode HTTP, sehingga URL berbasis aksi membuat cache dan tooling kehilangan pegangan. Gunakan/catatandengan POST dan DELETE. - Membuka GraphQL tanpa batasan kedalaman kueri. Satu kueri berlapis-lapis dapat memaksa server mengeksekusi ribuan resolver sehingga mudah dijadikan sarana beban berlebih. Tegakkan batas kedalaman dan kompleksitas di lapisan server.
- Menyamakan penghematan byte dengan penghematan waktu total. Payload biner yang kecil tetap harus antre, diproses, dan diuraikan, sehingga ukurannya dinilai bersama latensi ujung ke ujung.
Ringkasan#
- REST menyatukan kontrak dengan semantik HTTP dan paling cocok sebagai antarmuka publik, sedangkan gRPC memperketat kontrak melalui skema protobuf yang dikompilasi.
- GraphQL memindahkan penentuan bentuk respons kepada klien sehingga overfetching dan underfetching berkurang, dengan biaya caching HTTP yang sulit dan risiko kueri berlapis.
- Pada data yang sama, payload biner bergaya protobuf paling ramping, sedangkan jumlah pulang-pergi menentukan latensi ketika data tersebar di banyak endpoint.
Referensi#
- Architectural Styles and the Design of Network-based Software Architectures — Roy T. Fielding, UC Irvine — Disertasi asal istilah REST dan batasan arsitekturnya.
- gRPC Documentation — gRPC — Dokumentasi resmi gRPC mencakup pola panggilan dan skema protobuf.
- GraphQL Specification — GraphQL Foundation — Spesifikasi bahasa kueri dan sistem tipenya.