Belajar System Design - API Design & Best Practices
Episode 7 of 28

Belajar System Design - API Design & Best Practices

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

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

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 (Representational State Transfer)

REST adalah konvensi paling umum untuk API berbasis HTTP.

Resource Naming

plaintext
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 123

Prinsip: noun untuk resource, HTTP method untuk aksi. Hindari verb di URL (/getUser, /createOrder).

HTTP Status Code

CodeArtiKapan Pakai
200OKRead/update berhasil
201CreatedResource baru dibuat
204No ContentDelete berhasil (tanpa body)
400Bad RequestInput tidak valid
401UnauthorizedTidak terautentikasi
403ForbiddenTidak punya izin
404Not FoundResource tidak ada
409ConflictDuplikasi atau konflik data
429Too Many RequestsRate limit tercapai
500Internal Server ErrorServer error
503Service UnavailableService down/maintenance

Pagination

Untuk list yang panjang, gunakan pagination:

Pagination styles
# 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.

Idempotency

Idempotent artinya menjalankan operasi yang sama beberapa kali menghasilkan hasil yang sama.

MethodIdempotentAlasan
GETYaRead-only, tidak mengubah state
PUTYaUpdate ke value yang sama = hasil sama
DELETEYaHapus yang sudah hapus = tetap tidak ada
POSTTidakCreate duplikat jika dijalankan ulang
PATCHTergantungBisa idempotent jika patch value yang sama

Untuk POST yang tidak idempotent, gunakan idempotency key di header:

Idempotency key
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

GraphQL adalah query language untuk API yang memungkinkan client meminta tepat data yang dibutuhkan.

GraphQL query
query {
  user(id: "123") {
    name
    email
    orders(first: 5) {
      id
      total
      status
    }
  }
}

Kelebihan GraphQL

  • No over-fetching — client ambil tepat field yang dibutuhkan.
  • No under-fetching — satu query bisa ambil data dari beberapa resource.
  • Strongly typed — schema mendefinisikan semua type dan field.

N+1 Problem

N+1 problem di GraphQL
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.

DataLoader batching
Tanpa DataLoader: 1000 users → 1000 queries ke DB
Dengan DataLoader: 1000 users → 1 query ke DB (IN clause)

Kapan REST Lebih Baik dari GraphQL

  • API sederhana dengan resource-based access.
  • Caching lebih mudah (HTTP caching built-in).
  • File upload/download lebih straightforward.
  • Tim lebih familiar dengan REST.

gRPC

gRPC menggunakan Protocol Buffers (Protobuf) untuk serialization dan HTTP/2 untuk transport.

Protobuf

Proto definition
service UserService {
  rpc GetUser (GetUserRequest) returns (User);
  rpc ListUsers (ListUsersRequest) returns (stream User);
}
 
message User {
  string id = 1;
  string name = 2;
  string email = 3;
}

gRPC vs REST

AspekRESTgRPC
FormatJSON (text)Protobuf (binary)
TransportHTTP/1.1HTTP/2
SpeedMenengahLebih cepat (binary, multiplexing)
StreamingTidak nativeClient/Server/Bidirectional
BrowserNative supportButuh gRPC-Web
Use caseClient-facing APIInter-service communication

Kapan gRPC Mengungguli REST

  • Inter-service communication di microservices (high throughput, low latency).
  • Streaming data real-time (chat, live updates).
  • Strongly typed contract antar service.

Rate Limiting

Rate limiting melindungi API dari abuse dan traffic burst.

Token Bucket

Setiap client punya "bucket" berisi token. Setiap request mengurangi 1 token. Token ditambahkan secara periodik.

Token bucket
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)

Sliding Window

Sliding window counter
Window: 1 detik
Limit: 100 requests
 
Dalam 1 detik terakhir: 85 requests → boleh (sisa 15)
Dalam 1 detik terakhir: 100 requests → ditolak (429)

HTTP Headers

Rate limit headers
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1692163200
Retry-After: 30

Praktik: API E-Commerce

API design untuk e-commerce
# 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.

Penutup

Inti yang harus dibawa pulang:

  • REST: resource naming konsisten, HTTP status code yang benar, cursor-based pagination, idempotency key untuk POST.
  • GraphQL: no over/under-fetching, tapi N+1 problem harus diatasi dengan DataLoader.
  • gRPC: binary, cepat, streaming native — ideal untuk inter-service communication.
  • Rate limiting: token bucket atau sliding window, selalu expose 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!

Belajar System Design - API Design & Best Practices | Belajar System Design