CRUD singkatan dari Create, Read, Update, Delete: empat hal yang bisa kamu lakukan ke data. Bayangin buku stok di gudang Toko Buku. Kamu menulis baris baru waktu buku datang (Create), membaca isinya (Read), membetulkan angka stok (Update), dan mencoret buku yang gak dijual lagi (Delete). Di pelajaran HTTP Method kamu sudah tahu method mana untuk apa. Sekarang kita tulis kodenya: lima endpoint yang benar-benar mengubah data.
🧪 Tebakan cepat: handler
GET /books/:idmencari bukunya denganbooks.find((b) => b.id === req.params.id). Kamu memintaGET /books/1, dan buku nomor 1 ada. Server menjawab200atau404? Tebak dulu, jawabannya ada di bagian "Read satu: id dari URL itu teks".
Peta CRUD ke HTTP
| Operasi | Request | Kalau berhasil | Kalau id-nya gak ada |
|---|---|---|---|
| Create | POST /books |
201 Created + header Location |
|
| Read (daftar) | GET /books |
200 OK |
|
| Read (satu) | GET /books/2 |
200 OK |
404 Not Found |
| Update (ganti semua) | PUT /books/2 |
200 OK |
404 Not Found |
| Update (sebagian) | PATCH /books/2 |
200 OK |
404 Not Found |
| Delete | DELETE /books/2 |
204 No Content |
404 Not Found |
Datanya kita simpan di array dulu. Database menyusul di pelajaran Query Database.
let books = [
{ id: 1, title: "Laskar Pelangi", author_id: 1, price: 89000, stock: 12 },
{ id: 2, title: "Bumi Manusia", author_id: 2, price: 125000, stock: 0 },
{ id: 3, title: "Cantik Itu Luka", author_id: 3, price: 110000, stock: 5 },
];
let nextId = 4; // id untuk buku berikutnya
app.get("/books", (req, res) => {
res.json(books);
});
Read satu: id dari URL itu teks
app.get("/books/:id", (req, res) => {
const id = Number(req.params.id);
const book = books.find((b) => b.id === id);
if (!book) {
return res.status(404).json({ error: "Book not found" });
}
res.json(book);
});
:iddi path artinya bagian itu boleh diisi apa saja. Untuk/books/1,req.params.idberisi"1"."1"itu teks, bukan angka.1 === "1"hasilnyafalse, jadi tanpaNumber(...)(atauparseInt),findgak pernah ketemu dan semua id dijawab404. Itu jawaban tebakannya.findmengembalikanundefinedkalau gak ada yang cocok. Cek itu lalu jawab404.returnbikin handler berhenti di situ, supaya gak menjawab dua kali.
Create: POST, 201, dan Location
app.post("/books", (req, res) => {
const book = { ...req.body, id: nextId++ };
books.push(book);
res.set("Location", "/books/" + book.id);
res.status(201).json(book);
});
...req.bodymenyalin semua field kiriman ke object baru.- Server yang memilih id, dan
idditulis paling akhir supaya client gak bisa memilih id sendiri.nextId++memakai 4, lalu menaikkannya jadi 5 untuk buku berikutnya. res.set("Location", ...)memberi tahu client alamat buku barunya.res.status(201)memasang status sebelum.json(...)mengirim body.
Kirim POST /books dengan body {"title": "Ronggeng Dukuh Paruk", "author_id": 4, "price": 95000, "stock": 3}:
HTTP/1.1 201 Created
Location: /books/4
Content-Type: application/json; charset=utf-8
{"title":"Ronggeng Dukuh Paruk","author_id":4,"price":95000,"stock":3,"id":4}
Update: PUT mengganti, PATCH menambal
app.put("/books/:id", (req, res) => {
const id = Number(req.params.id);
const index = books.findIndex((b) => b.id === id);
if (index === -1) return res.status(404).json({ error: "Book not found" });
books[index] = { ...req.body, id };
res.json(books[index]);
});
app.patch("/books/:id", (req, res) => {
const id = Number(req.params.id);
const book = books.find((b) => b.id === id);
if (!book) return res.status(404).json({ error: "Book not found" });
Object.assign(book, req.body, { id });
res.json(book);
});
PUTmembuat object baru dari body saja. Field yang gak dikirim hilang.PATCHmenimpa field yang dikirim ke buku yang sudah ada (Object.assign), sisanya tetap.idditulis paling akhir di dua-duanya, supaya body{"id": 99}gak bisa mengganti nomor bukunya.
Body yang sama, {"stock": 7}, ke buku 2:
| Request | Buku 2 sesudahnya |
|---|---|
PATCH /books/2 |
{"id":2,"title":"Bumi Manusia","author_id":2,"price":125000,"stock":7} |
PUT /books/2 |
{"stock":7,"id":2} |
Jadi kalau cuma mau mengubah sebagian field, misalnya nama siswa saja, pakai PATCH. PUT dipakai kalau kamu mengirim seluruh isi datanya.
Delete: 204, lalu 404
app.delete("/books/:id", (req, res) => {
const id = Number(req.params.id);
const index = books.findIndex((b) => b.id === id);
if (index === -1) return res.status(404).json({ error: "Book not found" });
books.splice(index, 1);
res.status(204).send();
});
findIndexmemberi posisi bukunya, atau-1kalau gak ada.splice(index, 1)membuang satu isi array di posisi itu.204 No Contentartinya berhasil dan gak ada body.res.status(204).send()menutup response-nya tanpa isi.
DELETE /books/3 pertama dijawab 204. Yang kedua dijawab 404, dan GET /books/3 juga 404, karena bukunya sudah gak ada.
⚠️ Jebakan yang sering ketemu
- Membandingkan id teks dengan angka.
b.id === req.params.idselalufalse. Ubah dulu denganNumber(...)atauparseInt(...). - Create dijawab 200.
res.json(book)saja berstatus200. Data baru dijawabres.status(201).json(...). - Lupa 404. Tanpa cek
if (!book), id yang gak ada dijawab200dengan body kosong. Lebih parah lagi di delete: tanpa cekindex === -1,books.splice(-1, 1)menghapus buku terakhir, karena-1artinya "dari belakang". - Body di 204.
res.status(204).json({ message: "Deleted" })gak mengirim apa-apa: Express membuang body dari response204. Mau mengirim konfirmasi? Pakai200dengan body.
TL;DR: CRUD = Create, Read, Update, Delete. Create: POST /books, { ...req.body, id: nextId++ }, push, header Location, res.status(201). Read: GET /books untuk daftar, GET /books/:id dengan Number(req.params.id) dan find, karena id dari URL itu teks. Update: PUT mengganti seluruh isi (field yang gak dikirim hilang), PATCH hanya mengubah yang dikirim; untuk update sebagian, pakai PATCH. Delete: findIndex lalu splice, jawab res.status(204).send() tanpa body. Id yang gak ada dijawab 404 di read, update, dan delete, termasuk DELETE kedua untuk buku yang sama.