Tentukan spesifikasi API
Tulis kontrak API yang bisa dites: resource, error, validasi, versioning, dan perubahan apa yang aman tanpa mematahkan klien lama.
Pertanyaan
Apa yang dijanjikan API-mu ke klien, dan perubahan apa yang boleh kamu lakukan tanpa mematahkan klien yang sudah jalan?
Selesai baca ini, kamu bisa menulis spesifikasi satu endpoint lengkap dengan bentuk error, aturan validasi, dan aturan kompatibilitas, lalu menilai apakah sebuah perubahan itu aman.
Prasyarat dan contoh
Kamu paham request dan respons HTTP (method, status code, header) dari M02, dan sudah pernah bikin endpoint JSON.
Contoh: tim pembayaran punya endpoint POST /v1/orders yang dipanggil aplikasi mobile, web, dan
dua layanan internal. Suatu hari backend mengganti field total dari angka rupiah (125000)
jadi objek { "amount": 125000, "currency": "IDR" }. Server baru lolos semua tes. Aplikasi
mobile versi lama, yang masih dipakai 30% pengguna, crash karena mengharapkan angka.
Implementasinya benar, kontraknya yang rusak. Kontrak API itu janji yang ditulis, dan klien bergantung ke janji itu, termasuk bagian yang kamu nggak sadar sudah kamu janjikan.
Mekanisme
Spesifikasi yang baik menjawab lima hal untuk tiap endpoint.
1. Resource dan operasinya. Namai benda, bukan aksi: /orders, /orders/{id}. Tiap
method punya arti tetap. GET nggak mengubah apa pun. PUT dan DELETE idempoten secara
semantik (diulang hasilnya sama). POST nggak punya jaminan itu, dan itu jadi bahan lesson
M03.05.
2. Bentuk respons sukses. Tulis tiap field, tipe, satuan, dan apakah boleh null. "total"
tanpa satuan adalah bug yang menunggu. Tulis juga status code yang berbeda untuk keadaan
berbeda: 201 untuk dibuat, 200 untuk dibaca, 204 untuk sukses tanpa isi.
3. Bentuk error. Satu format untuk semua error, supaya klien bisa menanganinya tanpa menebak:
{
"error": {
"code": "insufficient_stock",
"message": "Stok tidak cukup untuk SKU A-100",
"retryable": false
}
}
Klien mengambil keputusan dari code dan status HTTP, bukan dari message (teks boleh
berubah). Bedakan tiga kelas: 4xx salah di klien (jangan diulang tanpa mengubah request),
429 terlalu cepat (tunggu dulu), 5xx salah di server (mungkin aman diulang, lihat
M03.04).
4. Validasi. Tulis batasnya: quantity bilangan bulat 1 sampai 100, note maksimal 500
karakter, body maksimal 64 KB. Pilih sikap untuk field yang nggak dikenal (ditolak atau
diabaikan) dan tulis. Tolak dengan 422 atau 400 plus daftar field yang salah, bukan satu
pesan umum.
5. Versioning dan kompatibilitas. Bedakan perubahan yang aman dan yang mematahkan:
| Perubahan | Aman untuk klien lama? |
| --- | --- |
| Menambah field opsional di respons | Ya, kalau klien mengabaikan field asing |
| Menambah endpoint baru | Ya |
| Menambah nilai baru pada enum respons | Berisiko: klien dengan switch ketat bisa gagal |
| Menghapus atau mengganti nama field | Tidak |
| Mengubah tipe atau satuan field | Tidak |
| Membuat field request opsional jadi wajib | Tidak |
Aturan praktis: respons boleh bertambah, nggak boleh berubah arti. Kalau perubahan yang
mematahkan memang perlu, keluarkan versi baru (/v2/orders atau header versi), jalankan
keduanya sambil memantau berapa klien yang masih memakai versi lama, lalu tetapkan tanggal
pensiun.
Saat rancangan ini gagal
- Kontrak implisit. Urutan elemen array, field yang kebetulan selalu ada, atau pesan error yang diparse klien dengan regex. Semuanya dianggap janji oleh klien walaupun nggak pernah kamu tulis. Cara menahannya: tes kontrak yang membandingkan respons nyata dengan skema.
- Error yang nggak bisa dibedakan. Semua kegagalan jadi
500dengan pesan teks. Klien nggak tahu mana yang aman diulang, jadi memilih salah satu ekstrem: nggak pernah retry, atau selalu retry termasuk untuk request yang memang salah. - Versi yang nggak pernah pensiun. Kamu mendukung
v1,v2,v3selamanya, dan tiap perbaikan bug harus dikerjakan tiga kali. Tanpa tanggal pensiun dan data pemakaian per versi, biaya ini nggak terlihat sampai terlambat. - Validasi hanya di klien. Klien mobile membatasi
quantitysampai 100, tapi API nggak. Klien lain mengirimquantity: 1000000dan menghabiskan stok.
Latihan
Kamu mengelola GET /v1/products/{id} yang mengembalikan:
{ "id": "p1", "name": "Kopi", "price": 25000, "status": "active" }
Nilai tiap perubahan berikut: aman, berisiko, atau mematahkan. Sebutkan alasannya.
- Menambah field
weight_grams. - Mengganti
pricedari rupiah ke sen (2500000). - Menambah nilai
statusbaru:"discontinued". - Mengganti nama
namejadititle. - Mengembalikan
404untuk produk yang dihapus, yang tadinya200denganstatus: "deleted".
Lihat pembahasan
- Aman. Field baru di respons, asalkan kontrak awal menyatakan klien mengabaikan field asing.
- Mematahkan. Tipe sama, arti berubah. Klien menampilkan harga 100 kali lebih besar tanpa
error apa pun, yang lebih berbahaya daripada crash. Solusinya field baru (
price_minor) di sampingprice. - Berisiko. Klien yang menangani
activedandeletedsaja mungkin jatuh ke cabang default. Kontrak sebaiknya dari awal menulis "klien harus menangani nilai status yang belum dikenal". Kalau belum, umumkan dulu sebelum mengirim nilai baru. - Mematahkan. Nama field berubah. Tambahkan
title, pertahankanname. - Mematahkan. Status code adalah bagian kontrak. Klien yang membaca
status: "deleted"sekarang menerima error. Perubahan semacam ini harus lewat versi baru.
Polanya: tanya "klien lama yang nggak diubah sama sekali, apa yang terjadi?" Kalau jawabannya "salah diam-diam" (nomor 2), itu lebih buruk daripada "gagal keras".
Terkait
- Lanjut ke M03.02 Rancang pagination, satu bagian kontrak yang sering salah untuk koleksi besar.
- Bentuk error dan flag
retryabledipakai lagi di M03.04. - Lab L03 dan proyek B01 belum tersedia.
Catatan
- Asumsi: API JSON di atas HTTP, dengan klien yang nggak bisa kamu paksa upgrade (mobile, integrasi pihak ketiga).
- Yang nggak dibahas: GraphQL, gRPC, dan tooling skema seperti OpenAPI, walaupun idenya sama.
- Tabel kompatibilitas itu panduan umum. Klien tertentu bisa lebih rapuh dari yang diasumsikan.
- Terakhir ditinjau: 26 Oktober 2026.