Mendesain REST API yang benar: modeling resource, pemilihan metode dan status code yang tepat, versioning, pagination, error format yang konsisten, dan best practices, dengan studi kasus desain API toko online

Setelah di episode 2 kalian memahami HTTP dan di episode 3 memilih bahasa, sekarang saatnya menjawab pertanyaan paling penting bagi seorang backend developer: bagaimana mendesain API yang enak dipakai? Banyak API yang "berfungsi" tapi menyakitkan bagi konsumennya — status code salah, format error tidak konsisten, resource yang membingungkan.
Mengapa desain API penting? Karena API adalah kontrak jangka panjang. Sekali dipakai mobile app dan partner bisnis, mengubahnya berarti melanggar janji. Desain yang buruk memaksa tim lain menulis workaround; desain yang baik membuat integrasi mengalir. Episode ini membangun fondasi desain REST yang akan kalian pakai sepanjang series dan di dunia kerja.
REST (Representational State Transfer) sederhananya: modelkan dunia nyata sebagai resource, dan akses resource itu lewat URL + metode HTTP. Prinsip kuncinya:
products, orders), bukan kata kerja (getProducts, createOrder).GET, POST, PUT, PATCH, DELETE).Pertama, identifikasi resource inti dari domain bisnis. Untuk toko online kita:
| Resource | Representasi | Contoh URL |
|---|---|---|
| Produk | products | /products, /products/42 |
| Order | orders | /orders, /orders/87 |
| User | users | /users/5 |
| Item dalam order | Nested | /orders/87/items |
| Ulasan produk | Nested | /products/42/reviews |
Aturan emas nested resource: gunakan nesting hanya untuk resource yang tidak berdiri sendiri (item order tanpa order tidak masuk akal). Kalau resource hidup mandiri, jangan paksa nesting.
Pola CRUD standar untuk satu resource products:
| Metode | URL | Tujuan | Status sukses |
|---|---|---|---|
| GET | /products | Daftar produk | 200 |
| GET | /products/{id} | Detail produk | 200 |
| POST | /products | Buat produk | 201 |
| PUT | /products/{id} | Ganti produk | 200 |
| PATCH | /products/{id} | Ubah sebagian | 200 |
| DELETE | /products/{id} | Hapus produk | 204 |
Konsisten dengan tabel ini di seluruh resource. Jika semua resource mengikuti pola yang sama, konsumen API bisa menebak endpoint tanpa dokumentasi.
Tiga kesalahan paling umum dalam memilih status code:
400), belum login (401), tidak diizinkan (403), dan tidak ditemukan (404) punya makna berbeda — konsumen API bergantung pada perbedaan itu untuk logic.POST /products yang berhasil harus 201 Created, bukan 200 OK — dan sebaiknya membawa header Location menunjuk resource baru.409. Konflik bisnis seperti stok habis saat checkout atau email sudah terdaftar adalah 409 Conflict, bukan 400.GET /products/999 → 404
POST /orders tanpa auth → 401
POST /orders stok habis → 409
POST /products (berhasil) → 201Konsumen API harus bisa memparsing error secara seragam. Tetapkan satu format dan gunakan di semua endpoint:
{
"error": "VALIDATION_FAILED",
"message": "Nama produk wajib diisi",
"details": [
{ "field": "name", "reason": "required" },
{ "field": "price", "reason": "must_be_positive" }
]
}error adalah kode mesin yang stabil (jangan diubah, karena dipakai untuk logic), message untuk dibaca manusia, dan details opsional untuk field-level error. Format konsisten ini dibangun menjadi error handler terpusat di episode 11.
Note
Gunakan kode mesin (VALIDATION_FAILED) alih-alih menebak-nebak string untuk parsing di client. String rawan typo dan berubah; kode mesin adalah kontrak. Ini juga menjadi dasar kontrak testing di episode 8.
Daftar resource yang panjang tidak boleh dikembalikan sekaligus. Dua pola paling umum:
# Berbasis page (sederhana, stabil)
GET /products?page=2&limit=20
# Berbasis cursor (konsisten saat data berubah)
GET /products?cursor=eyJpZCI6NDJ9&limit=20Cursor pagination lebih unggul untuk data yang sering berubah (produk baru masuk terus): posisi tidak "melompat" seperti page. Respons dengan metadata:
{
"data": [ { "id": 42, "name": "Keyboard" } ],
"meta": { "page": 2, "limit": 20, "total": 103 }
}API yang sudah dipakai publik suatu saat harus berubah. Versioning memberi ruang untuk berubah tanpa merusak konsumen lama. Pendekatan paling umum adalah URL path versioning:
https://api.example.com/v1/products
https://api.example.com/v2/productsv1 dibiarkan hidup untuk konsumen lama; v2 membawa kontrak baru. Kebijakan penting: beri masa tenggang dan jadwalkan deprecation. Mulai v1 sejak API pertama dirilis — menambah versioning belakangan jauh lebih menyakitkan.
Tip
Jangan versioning terlalu dini (saat masih berubah tiap sprint) dan jangan terlalu lambat (saat 50 konsumen sudah terikat). Aturan praktis: buka v1 begitu API dipakai tim luar atau produksi, dan komunikasikan deprecation minimal 6 bulan sebelumnya.
Mari desain API lengkap untuk toko online kita. Skema resource:
/users → daftar & kelola akun
/users/{id} → detail akun
/products → daftar & kelola produk
/products/{id} → detail produk
/products/{id}/reviews → ulasan per produk
/orders → buat & lihat order
/orders/{id} → detail order
/orders/{id}/items → item dalam orderDesain beberapa flow:
curl -s http://localhost:3000/v1/products?limit=10
curl -s -X POST http://localhost:3000/v1/orders \
-H "Content-Type: application/json" \
-d '{"items":[{"productId":1,"qty":2}]}'
curl -s http://localhost:3000/v1/orders/87Flow ini: GET produk → POST order → GET detail order. Ketiga endpoint mengikuti pola tabel CRUD di atas — konsumen sudah bisa menebak tanpa dokumentasi.
/getProducts atau /createOrder adalah pelanggaran REST — method HTTP sudah menyatakan aksi. Gunakan GET /products dan POST /orders.
Client retry saat network putus bisa membuat order ganda. Solusi umum: idempotency key — client mengirim header Idempotency-Key, server menolak duplikat dengan 409. Episode 10 membahas ini untuk pembayaran.
/users/5/orders/87/items/3 terlalu dalam. Batasi nesting dua level; untuk resource yang hidup mandiri, gunakan root resource dengan filter.
Episode 4 membangun kontrak API: prinsip REST dengan resource sebagai kata benda, pola CRUD yang konsisten, status code yang tepat, format error seragam, pagination, dan versioning — diterapkan pada desain API toko online.
Inti yang harus dibawa pulang:
201 untuk create, 401 vs 403 untuk auth, 409 untuk konflik./v1, /v2) memberi ruang berubah tanpa merusak konsumen.Di episode 5 selanjutnya kita akan menata rumah data: SQL & database dasar — modeling skema, query, indexing, dan transaction, lengkap dengan praktik membangun skema produk dan order di PostgreSQL. Sampai jumpa di episode 5!