Siti sedang merapikan daftar tugas toko buku: menambah tugas baru, mengganti isi tugas yang lama, memperbaiki satu field saja, lalu menghapus yang sudah selesai. Setiap aksi itu punya padanan kata kerja HTTP — dan memilih kata kerja yang tepat membuat maksud permintaan terbaca jelas oleh server, rekan tim, dan tooling. Chapter ini menuntaskan kelima metode dengan satu skenario daftar tugas yang sama, plus konsep idempotensi yang menentukan aman atau tidaknya sebuah permintaan diulang.
Contoh kodenya kembali memakai pola server tiruan tertanam: server kecil node:http dengan “database” berupa array di memori, lengkap dengan lima rute metode. Jalankan node crud-tugas.mjs dan keluarannya akan persis seperti yang tercetak di bawah.
Lima Metode dan Tugasnya #
Konvensi industri membagi peran kelima metode dengan tegas:
| Metode | Tugas lazim | Respons sukses yang umum |
|---|---|---|
GET | Membaca data, tidak mengubah apa pun | 200 OK |
POST | Membuat sumber daya baru | 201 Created |
PUT | Mengganti sumber daya secara utuh | 200 OK |
PATCH | Memperbarui sebagian field | 200 OK |
DELETE | Menghapus sumber daya | 204 No Content |
Kaitannya dengan idempotensi — sifat “dijalankan berkali-kali, hasilnya sama seperti sekali”:
GETbersifat safe: membaca saja, tidak mengubah data.GET,PUT, danDELETEidempoten — diulang tiga kali pun keadaan akhirnya sama.POSTtidak idempoten: mengirimPOST /tugastiga kali biasanya membuat tiga data baru.PATCHidempotensinya bergantung implementasi; penggantian nilai tetap (selesai: true) idempoten, operasi relatif (tambah 1) tidak.
Sifat ini bukan teori semata: mesin retry (chapter Retry dan Timeout Strategies) hanya berani mengulang permintaan yang aman diulang. Atauannya sederhana namun tegas — GET, PUT, dan DELETE boleh dicoba lagi begitu saja, sedangkan POST menuntut kehati-hatian tambahan sebelum pengulangan dilakukan.
Satu Berkas, Lima Metode #
Berkas berikut menghidupkan seluruh skenario Siti — dari POST hingga GET pengecekan akhir. Bagian server mengurus lima rute; bagian client menjalankan aksinya berurutan:
// crud-tugas.mjs
// Lima metode HTTP terhadap daftar tugas: GET, POST, PUT, PATCH, DELETE.
// Jalankan di terminal: node crud-tugas.mjs (tanpa internet).
import http from "node:http";
// "Database" tiruan di memori server.
let tugas = [
{ id: 1, judul: "Rapikan rak buku", selesai: false },
{ id: 2, judul: "Cetak label harga", selesai: false },
];
let idBerikut = 3;
// Helper: mengumpulkan body permintaan sampai selesai, lalu menguraikan JSON-nya.
const bacaBody = (req) =>
new Promise((resolve) => {
let raw = "";
req.on("data", (potongan) => (raw += potongan));
req.on("end", () => resolve(JSON.parse(raw)));
});
const server = http.createServer(async (req, res) => {
const kirim = (status, isi) => {
res.writeHead(status, { "Content-Type": "application/json" });
res.end(isi === undefined ? "" : JSON.stringify(isi));
};
if (req.method === "GET" && req.url === "/tugas") return kirim(200, tugas);
if (req.method === "POST" && req.url === "/tugas") {
const baru = { id: idBerikut++, ...(await bacaBody(req)) };
tugas.push(baru);
return kirim(201, baru);
}
const id = Number(req.url.split("/")[2]);
const satu = tugas.find((t) => t.id === id);
if (req.method === "PUT" && satu) {
const body = await bacaBody(req);
tugas = tugas.map((t) => (t.id === id ? { ...body, id } : t));
return kirim(200, tugas.find((t) => t.id === id));
}
if (req.method === "PATCH" && satu) {
Object.assign(satu, await bacaBody(req));
return kirim(200, satu);
}
if (req.method === "DELETE" && satu) {
tugas = tugas.filter((t) => t.id !== id);
return kirim(204);
}
kirim(404, { pesan: "rute tidak cocok" });
});
server.listen(0, "127.0.0.1", async () => {
const port = server.address().port;
const dasar = `http://127.0.0.1:${port}`;
// POST: menambah data baru.
const buat = await fetch(`${dasar}/tugas`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ judul: "Hitung kas harian", selesai: false }),
});
const baru = await buat.json();
console.log("POST ->", buat.status, "| id baru:", baru.id);
// PUT: mengganti data utuh (field lama yang tidak dikirim ikut hilang).
const ganti = await fetch(`${dasar}/tugas/2`, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ judul: "Cetak label harga baru", selesai: false }),
});
const utuh = await ganti.json();
console.log("PUT ->", ganti.status, "| field sekarang:", Object.keys(utuh).join(", "));
// PATCH: memperbarui sebagian field saja.
const ubah = await fetch(`${dasar}/tugas/2`, {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ selesai: true }),
});
const sebagian = await ubah.json();
console.log("PATCH ->", ubah.status, "|", sebagian.judul, "selesai:", sebagian.selesai);
// DELETE: menghapus; respons 204 tanpa body.
const hapus = await fetch(`${dasar}/tugas/1`, { method: "DELETE" });
console.log("DELETE->", hapus.status, "| isi body:", JSON.stringify(await hapus.text()));
// GET: memastikan keadaan akhir daftar.
const daftar = await (await fetch(`${dasar}/tugas`)).json();
console.log("GET ->", daftar.map((t) => `${t.id}:${t.judul}${t.selesai ? " (selesai)" : ""}`).join(" | "));
server.closeAllConnections();
server.close();
});Output yang Dihasilkan:
POST -> 201 | id baru: 3
PUT -> 200 | field sekarang: judul, selesai, id
PATCH -> 200 | Cetak label harga baru selesai: true
DELETE-> 204 | isi body: ""
GET -> 2:Cetak label harga baru (selesai) | 3:Hitung kas harianMari kita telaah baris-baris pentingnya:
- Baris 31 (
{ id: idBerikut++, ...(await bacaBody(req)) }): serverPOSTmenomori data baru sendiri, lalu menyebarkan isi body di belakangnya. Server yang menentukanid— bukan client — karena server pemegang sumber kebenaran. - Baris 39–42 (rute
PUT): data lama diganti seluruhnya oleh{ ...body, id }. Bukti terlihat pada keluaranPUT: field yang tersisa hanyajudul, selesai, id— isi baru memang tidak memuat apa pun lagi selain itu. - Baris 44–46 (rute
PATCH):Object.assignmenimpa hanya field yang dikirim. SetelahPATCH, tugas nomor 2 masih bernama “Cetak label harga baru” (warisanPUT) tetapiselesaiberubah menjaditrue. - Baris 48–50 (rute
DELETE): item disaring keluar dari array dan server menjawab204 No Content. KeluaranDELETEmemperlihatkan konsekuensinya: body respons benar-benar string kosong, sehingga kita membacanya dengantext(), bukanjson(). - Baris 60–64 (
POSTdi sisi client): optionsfetchberisimethod: "POST", headerContent-Type: application/json, dan body hasilJSON.stringify— tiga serangkai yang berulang untukPUTdanPATCH. - Baris 87 (
fetch(..., { method: "DELETE" })): permintaan hapus tanpa options tambahan; URL/tugas/1sudah menyebut sumber daya yang dimaksud. - Baris 91–92 (verifikasi
GET): daftar akhir tinggal dua tugas — nomor 1 sudah terhapus, nomor 2 telah berganti judul dan ditandai selesai, nomor 3 adalah buahPOST.
Menyusun URL Sumber Daya #
Metode menyatakan aksi; URL menyatakan sumber dayanya. Konvensi REST yang dominan mengatur keduanya menjadi pola yang konsisten:
- URL koleksi memakai kata jamak:
/tugasuntuk seluruh daftar. - URL item menyertakan identitas:
/tugas/2untuk satu tugas. - Aksi mengikuti metode, bukan kata kerja di URL: menghapus tugas 1 ditulis
DELETE /tugas/1— bukanGET /hapusTugas?id=1.
Pola ini yang dipakai berkas contoh: GET /tugas membaca daftar, sedangkan PUT /tugas/2, PATCH /tugas/2, dan DELETE /tugas/1 berbicara ke satu item. Bila aksi tidak masuk akal sebagai pengubahan data — “tandai semua selesai”, misalnya — banyak API menambahkan sub-sumber daya seperti PATCH /tugas/tandai-semua atau mengizinkan PATCH pada koleksi. Penyaringan data tetap tugas query parameter (/buku?genre=sejarah&page=2) — pola yang menjadi pondasi chapter Pagination.
Memilih antara PUT dan PATCH #
Keduanya “mengubah data”, tetapi kontraknya berbeda:
PUTmenggantikan seluruh isi. Client bertanggung jawab mengirim versi lengkap; field yang terlewat ikut hilang dari data. Cocok ketika client memang memegang bentuk utuh data, misalnya formulir sunting penuh.PATCHmenambal sebagian. Client hanya mengirim field yang berubah; field lain tak tersentuh. Cocok untuk aksi ringkas seperti menandai selesai atau mengubah satu kolom.
Kesalahan yang lumayan sering terjadi: memakai PUT dengan body setengah jalan, lalu bingung karena field lain hilang. Anggap PUT seperti mengganti selembar kertas dengan lembar baru, dan PATCH seperti menyunting satu baris di lembar lama.
Metode dan Status yang Cocok #
Pasangan metode–status yang lazim layak dihafal karena muncul di hampir semua API: GET → 200, POST → 201, perubahan data sukses → 200, DELETE → 204. Beberapa API menambahkan Location pada respons 201 yang berisi URL sumber daya baru, dan sebagian menjawab PUT/PATCH dengan 204 ketika tidak ada data balasan yang layak dikirim. Bila metode dikirim tanpa rute yang cocok, server menjawab 405 Method Not Allowed — satu lagi anggota keluarga 4xx yang akan sering kita jumpai di chapter Response Status.
Dengan lima metode ini, hampir semua aksi CRUD sudah terwakili. Selanjutnya kita perlu fasih membaca kode status pada respons — termasuk memahami mengapa fetch tidak menganggap 404 sebagai galat.