API yang baik itu gampang ditebak: kalau kamu tahu satu endpoint-nya, kamu bisa menebak yang lain tanpa membuka dokumentasi. Bayangin denah perpustakaan yang rapi. Raknya diberi nama benda ("Novel", "Penulis"), nomor bukunya urut, dan cara meminjam di semua rak sama. Begitu paham satu rak, kamu paham semuanya. API yang asal-asalan seperti perpustakaan yang tiap raknya punya aturan sendiri.
🧪 Tebakan cepat: ada endpoint usulan
POST /deleteBook?id=9. Ada berapa masalah di situ? Tebak dulu, jawabannya ada di bagian "Kata benda jamak, method sebagai kata kerja".
Kata benda jamak, method sebagai kata kerja
Path menunjuk benda (resource), method menyebut aksinya. Kumpulannya ditulis jamak, satu bendanya ditunjuk dengan id di path:
| Kurang tepat | Lebih baik | Kenapa |
|---|---|---|
GET /getBooks |
GET /books |
GET sudah berarti "ambil" |
GET /book/1 |
GET /books/1 |
koleksinya jamak, bukunya nomor 1 di koleksi itu |
POST /books/create |
POST /books |
POST ke koleksi sudah berarti "buat" |
PUT /Books/7/Update_Stock |
PATCH /books/7 dengan body {"stock": 7} |
huruf kecil, tanpa kata kerja, ubah sebagian pakai PATCH |
POST /deleteBook?id=9 punya tiga masalah: ada kata kerja di URL (delete), method-nya POST padahal aksinya menghapus, dan id buku ditaruh di query, padahal satu benda ditunjuk lewat path. Versi rapinya DELETE /books/9. Itu jawaban tebakannya.
Pola yang sama berlaku untuk resource apa pun: semua postingan blog diambil dengan GET /api/posts, bukan GET /api/getPost.
Nama path juga dibuat seragam: huruf kecil semua, kata disambung tanda hubung kalau perlu (/best-sellers), tanpa akhiran .json.
Nesting secukupnya
Benda yang "milik" benda lain boleh ditulis bersarang (nested), cukup satu tingkat:
GET /authors/2/books
GET /books/7/reviews
GET /authors/2/books/7/reviews terlalu dalam. Buku 7 sudah punya alamat sendiri, jadi ulasannya cukup GET /books/7/reviews. Pegangannya: paling banyak satu induk di depan, /induk/id/anak.
Menyaring, mengurutkan, dan memotong per halaman gak butuh path baru, cukup query string, seperti di pelajaran URL & Endpoint dan Pagination: GET /books?author_id=2&sort=-price&page=2. Jangan bikin /getBooksByAuthor/2.
Response yang konsisten
Bentuk jawabannya sama di semua endpoint, jadi frontend cukup menulis satu cara membaca:
| Situasi | Status | Body |
|---|---|---|
| Daftar | 200 |
{"data": [...], "meta": {...}} |
| Satu benda | 200 |
{"data": {...}} |
| Dibuat | 201 |
{"data": {...}, "message": "Book created"} |
| Gak ketemu | 404 |
{"error": "Book not found"} |
app.get("/books/:id", (req, res) => {
const book = books.find((b) => b.id === Number(req.params.id));
if (!book) return res.status(404).json({ error: "Book not found" });
res.json({ data: book });
});
{ data: book }membungkus bukunya, sama seperti daftar yang dibungkus{ data: [...] }. Nanti kalau perlu info tambahan (misalnyameta), tinggal menambah key di sampingdatatanpa merusak client.- Nama key-nya jelas dan satu gaya. API Toko Buku memakai
snake_case(author_id), jadi semua key ikut gaya itu.
Response seperti {"s": 1, "d": [{"n": "Budi"}]} memang hemat beberapa byte, tapi nama key-nya gak deskriptif: orang yang membacanya harus menebak s, d, dan n itu apa. Tulis {"success": true, "data": [{"name": "Budi"}]}.
Versioning: /api/v1
API itu janji ke semua client. Kalau suatu hari bentuk response harus berubah, misalnya title diganti judul, aplikasi HP versi lama yang masih membaca title langsung rusak. Karena itu path diberi versi:
GET /api/v1/books
GET /api/v2/books
Perubahan yang merusak (breaking change) masuk ke /api/v2, sedangkan /api/v1 tetap jalan untuk client lama. Ini disebut menjaga backward compatibility. Detailnya ada di pelajaran API Versioning.
⚠️ Jebakan yang sering ketemu
- Kata kerja di URL.
/getBooks,/books/create,/deleteBook. Method sudah bilang aksinya. - Campur tunggal dan jamak.
/book/1di satu tempat,/booksdi tempat lain. Pilih jamak, di semua endpoint. - Nesting berlapis-lapis.
/authors/2/books/7/reviews/3susah dipakai dan diubah. Satu induk saja:/books/7/reviews, atau/reviews/3untuk satu ulasan. - Bentuk response beda-beda. Satu endpoint mengirim
{"data": ...}, yang lain mengirim array polos, yang lain lagi{"result": ...}. Satu bentuk untuk semua.
TL;DR: API yang baik gampang ditebak. Path berisi kata benda jamak (/books, /books/9), aksinya lewat method (GET, POST, PUT/PATCH, DELETE), jadi gak ada kata kerja di URL: POST /deleteBook?id=9 jadi DELETE /books/9. Huruf kecil, nesting paling banyak satu induk (/books/7/reviews), dan saring atau urutkan lewat query. Response-nya konsisten dan nama key-nya deskriptif: {"data": ...} untuk sukses, {"error": "..."} dengan status yang pas untuk gagal, 201 plus message untuk data baru. Beri versi di path (/api/v1/posts) supaya perubahan besar bisa masuk ke v2 tanpa merusak client lama (backward compatibility).