Setiap obrolan dengan API terdiri dari dua pesan: request dari client dan response dari server. Bentuk keduanya sama: baris pertama, header, baris kosong, lalu body. Bayangin kirim paket lewat kurir. Label di luar kotak (header) bilang isinya apa dan mau ke mana, kotaknya berisi barangnya (body), dan kurir pulang membawa tanda terima dengan satu cap (status code): diterima, alamat gak ketemu, atau ditolak karena kemasannya salah.
🧪 Tebakan cepat: client mengirim body JSON yang benar, tapi lupa header
Content-Type: application/json. Apa yang terjadi di server? Tebak dulu, jawabannya ada di bagian "Header: label di luar kotak".
Isi sebuah request
Admin Toko Buku menambah buku baru:
POST /books HTTP/1.1
Host: api.tokobuku.test
Content-Type: application/json
Accept: application/json
{"title": "Ronggeng Dukuh Paruk", "author_id": 4, "price": 95000, "stock": 8}
- Baris pertama: method, path, dan versi HTTP.
- Header: satu per baris,
Nama: nilai. - Baris kosong memisahkan header dari body.
- Body: datanya.
GETbiasanya gak punya body.
Data bisa ikut di empat tempat, dan tiap tempat punya tugas:
| Tempat | Contoh | Tugasnya |
|---|---|---|
| path | /books/4 |
menunjuk benda yang mana |
| query | ?author_id=2 |
pilihan untuk daftar |
| header | Content-Type: application/json |
keterangan tentang request-nya |
| body | {"title": "..."} |
data yang dikirim (POST, PUT, PATCH) |
Header: label di luar kotak
Content-Typemenyebut format body yang dikirim. Untuk JSON:application/json.Acceptmenyebut format jawaban yang diinginkan.- Nanti ada
Authorizationuntuk token login, di pelajaran autentikasi.
Server membaca Content-Type dulu sebelum membuka body. Tanpa application/json, server gak tahu body-nya JSON, jadi body-nya gak di-parse. Itu jawaban tebakannya. Di Express, express.json() cuma membaca body yang Content-Type-nya JSON, jadi req.body kosong dan validasi bilang title gak ada. Server yang lebih ketat langsung menjawab 415 Unsupported Media Type. Hasilnya sama: data yang kamu kirim gak terbaca.
Jebakannya, fetch dengan body teks dan tanpa header mengirim Content-Type: text/plain;charset=UTF-8. Jadi tulis header-nya sendiri:
fetch("https://api.tokobuku.test/books", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ title: "Ronggeng Dukuh Paruk", author_id: 4, price: 95000, stock: 8 }),
});
Status code: jawaban dalam tiga angka
Angka pertama menyebut kelompoknya: 2xx berhasil, 3xx pindah alamat (redirect), 4xx salahnya di request (client), 5xx salahnya di server.
| Status | Nama | Kapan, di API Toko Buku |
|---|---|---|
200 |
OK | GET /books/1 berhasil |
201 |
Created | POST /books membuat buku baru |
204 |
No Content | DELETE /books/2 berhasil, tanpa body |
400 |
Bad Request | body bukan JSON yang sah, atau field wajib gak ada |
404 |
Not Found | GET /books/99 |
405 |
Method Not Allowed | PUT /books |
415 |
Unsupported Media Type | body dikirim tanpa Content-Type: application/json |
422 |
Unprocessable Content | JSON-nya terbaca, tapi nilainya gak masuk akal, misalnya "price": -5000 |
500 |
Internal Server Error | bug di server |
200 artinya berhasil, 404 artinya Not Found: yang diminta gak ada. Batas 400 dan 422 gak dipatok mati. Banyak API memakai 400 untuk semua input yang salah, sebagian (Laravel, misalnya) memakai 422 untuk semua yang gagal validasi. Toko Buku memakai 400 untuk body yang gak bisa dibaca atau kehilangan field wajib, dan 422 untuk nilai yang gak masuk akal. Pilih satu aturan, lalu konsisten.
Bentuk body yang konsisten
Client paling senang kalau semua endpoint menjawab dengan bentuk yang sama. Di Toko Buku, jawaban berhasil berisi datanya langsung, dan jawaban gagal selalu berbentuk {"error": "..."}. Banyak API lain membungkus jawaban berhasil jadi {"data": ..., "message": "..."}. Latihan ini memakai bentuk bungkus itu. Bentuk mana pun boleh, asal satu bentuk dipakai di semua endpoint.
Latihan: POST /register
Ini endpoint tambah buku di Toko Buku:
app.post("/books", (req, res) => {
const { title, price } = req.body;
if (!title) {
return res.status(400).json({ error: "title is required" });
}
res.status(201).json({ id: 4, title, price });
});
const { title, price } = req.body;mengambil dua field dari body.if (!title)bernilaitruekalautitlegak ada atau teks kosong"".returnmenghentikan function. Tanpareturn, baris di bawahnya ikut jalan dan mengirim jawaban kedua. Di tester latihan, jawaban kedua itu menimpa yang pertama, jadi status400berubah jadi201.res.status(201).json(...)mengirim jawaban berhasil.
Latihannya sama bentuknya: endpoint POST /register, field name dan email. Tanpa name, jawab 400 dengan {"error": "Name is required"}. Kalau lengkap, jawab 201 dengan {"data": {"name": ..., "email": ...}, "message": "Registered!"}.
⚠️ Jebakan yang sering ketemu
- Error dengan status 200.
res.json({ error: "..." })tanpares.statustetap200, dan client mengira berhasil. Pasangres.status(400)dulu. - Lupa
returnsetelah mengirim error. Kode di bawahnya tetap jalan dan mengirim jawaban kedua. - Lupa
Content-Type. Body JSON yang terkirim sebagaitext/plaingak di-parse server. 500untuk kesalahan client. Input yang salah itu4xx.500artinya server-mu yang punya bug.
TL;DR: Request = baris pertama (method, path), header, baris kosong, body; response = status line, header, body. Path menunjuk benda, query mengatur daftar, header memberi keterangan, body membawa data. Content-Type: application/json wajib saat mengirim body JSON; tanpa itu server gak mem-parse body (atau menjawab 415), dan fetch tanpa header mengirim text/plain. Status: 2xx berhasil (200 OK, 201 Created, 204 No Content), 4xx salah di request (400 Bad Request, 404 Not Found, 405, 415, 422), 5xx salah di server. Kirim error dengan res.status(...) lalu return, dan pakai satu bentuk body di semua endpoint.