Memahami REST (resource naming, pagination, idempotency), GraphQL (schema-first, N+1 problem, DataLoader), gRPC (Protobuf, HTTP/2, streaming), dan rate limiting (token bucket, sliding window) untuk desain API production

Setelah di episode 6 kita memahami message queue dan event streaming, pada episode ini kita masuk ke kontrak antara dunia luar dan sistem kita: API design. API yang baik membuat integrasi mudah, developer experience menyenangkan, dan sistem bisa berkembang tanpa breaking changes. API yang buruk menciptakan friction, bug, dan frustrasi.
Di era microservices, setiap layanan berkomunikasi lewat API — baik internal (inter-service) maupun external (client-facing). Desain API yang konsisten dan well-documented adalah salah satu investment paling berdampak yang bisa dilakukan tim engineering.
REST adalah konvensi paling umum untuk API berbasis HTTP.
GET /users → list users
GET /users/123 → get user 123
POST /users → create user
PUT /users/123 → update user 123
DELETE /users/123 → delete user 123
GET /users/123/orders → orders of user 123Prinsip: noun untuk resource, HTTP method untuk aksi. Hindari verb di URL (/getUser, /createOrder).
| Code | Arti | Kapan Pakai |
|---|---|---|
| 200 | OK | Read/update berhasil |
| 201 | Created | Resource baru dibuat |
| 204 | No Content | Delete berhasil (tanpa body) |
| 400 | Bad Request | Input tidak valid |
| 401 | Unauthorized | Tidak terautentikasi |
| 403 | Forbidden | Tidak punya izin |
| 404 | Not Found | Resource tidak ada |
| 409 | Conflict | Duplikasi atau konflik data |
| 429 | Too Many Requests | Rate limit tercapai |
| 500 | Internal Server Error | Server error |
| 503 | Service Unavailable | Service down/maintenance |
Untuk list yang panjang, gunakan pagination:
# Offset-based (simple tapi inefficient untuk deep page)
GET /users?offset=100&limit=20
# Cursor-based (recommended untuk large dataset)
GET /users?cursor=eyJpZCI6MTAwfQ&limit=20
# Response
{
"data": [...],
"pagination": {
"next_cursor": "eyJpZCI6MTIwfQ",
"has_more": true
}
}Cursor-based lebih efisien untuk dataset besar karena tidak perlu offset scan.
Idempotent artinya menjalankan operasi yang sama beberapa kali menghasilkan hasil yang sama.
| Method | Idempotent | Alasan |
|---|---|---|
| GET | Ya | Read-only, tidak mengubah state |
| PUT | Ya | Update ke value yang sama = hasil sama |
| DELETE | Ya | Hapus yang sudah hapus = tetap tidak ada |
| POST | Tidak | Create duplikat jika dijalankan ulang |
| PATCH | Tergantung | Bisa idempotent jika patch value yang sama |
Untuk POST yang tidak idempotent, gunakan idempotency key di header:
POST /payments
Idempotency-Key: abc-123-def-456
Content-Type: application/json
{"amount": 150000, "currency": "IDR"}Server menyimpan idempotency key → jika request yang sama datang lagi, return response yang sama tanpa memproses ulang.
GraphQL adalah query language untuk API yang memungkinkan client meminta tepat data yang dibutuhkan.
query {
user(id: "123") {
name
email
orders(first: 5) {
id
total
status
}
}
}query { users { orders { items { product { name } } } } }
→ 1 query untuk users
→ N queries untuk orders (satu per user)
→ N queries untuk items (satu per order)
→ N queries untuk products (satu per item)
= 3N + 1 queries!Solusi: DataLoader — batch dan cache request dalam satu tick event loop.
Tanpa DataLoader: 1000 users → 1000 queries ke DB
Dengan DataLoader: 1000 users → 1 query ke DB (IN clause)gRPC menggunakan Protocol Buffers (Protobuf) untuk serialization dan HTTP/2 untuk transport.
service UserService {
rpc GetUser (GetUserRequest) returns (User);
rpc ListUsers (ListUsersRequest) returns (stream User);
}
message User {
string id = 1;
string name = 2;
string email = 3;
}| Aspek | REST | gRPC |
|---|---|---|
| Format | JSON (text) | Protobuf (binary) |
| Transport | HTTP/1.1 | HTTP/2 |
| Speed | Menengah | Lebih cepat (binary, multiplexing) |
| Streaming | Tidak native | Client/Server/Bidirectional |
| Browser | Native support | Butuh gRPC-Web |
| Use case | Client-facing API | Inter-service communication |
Rate limiting melindungi API dari abuse dan traffic burst.
Setiap client punya "bucket" berisi token. Setiap request mengurangi 1 token. Token ditambahkan secara periodik.
Bucket size: 10 tokens
Refill rate: 2 tokens/detik
Request 1-10: langsung proses (tokens 10 → 0)
Request 11: tunggu refill (tokens 0)
Request 12-13: tunggu 1 detik (tokens refill 2)Window: 1 detik
Limit: 100 requests
Dalam 1 detik terakhir: 85 requests → boleh (sisa 15)
Dalam 1 detik terakhir: 100 requests → ditolak (429)HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1692163200
Retry-After: 30# REST untuk CRUD
GET /api/v1/products → list products (pagination)
GET /api/v1/products/:id → detail product
POST /api/v1/orders → create order (idempotency key)
GET /api/v1/users/:id/orders → user's orders
# GraphQL untuk complex query
POST /graphql
{
"query": "{ user(id: \"123\") { name orders { items { product { name price } } } } }"
}
# gRPC untuk inter-service
service OrderService {
rpc CreateOrder (CreateOrderRequest) returns (Order);
rpc StreamOrderEvents (StreamRequest) returns (stream OrderEvent);
}Tip
Untuk system design interview, selalu tanyakan apakah API untuk client-facing (REST/GraphQL) atau inter-service (gRPC). Kebutuhan streaming menunjuk ke gRPC, complex query menunjuk ke GraphQL, dan simple CRUD cukup REST.
Inti yang harus dibawa pulang:
X-RateLimit-* headers.Di episode 8 selanjutnya kita akan membahas storage, blob & CDN — object storage (S3/R2/GCS), CDN edge caching, consistent hashing, dan desain sistem upload gambar dari client ke edge. Storage adalah fondasi data yang tidak termasuk di database — file, gambar, video, dan aset lainnya!