Koneksi sudah tersedia — sekarang aplikasi toko buku kita perlu bekerja: mencatat judul baru, menampilkan katalog, mengubah harga, dan menghapus stok lama. Empat operasi itu adalah CRUD, dan di driver Node.js semuanya berbentuk method pada handle collection: insertOne(), find(), updateOne(), hingga deleteMany(). Dua hal khas sisi JavaScript perlu dikuasai: setiap operasi tulis mengembalikan object hasil yang bisa diperiksa, dan _id yang dihasilkan driver bertipe ObjectId — bukan string. Chapter ini menjalankan seluruh siklus CRUD pada collection buku dengan data yang sama sepanjang Section.
Method dan Bentuk Hasilnya #
Setiap method tulis mengembalikan object ringkasan dari server. Field-field inilah yang dipakai aplikasi untuk memastikan pekerjaan benar-benar terjadi:
| Method | Field hasil yang penting |
|---|---|
insertOne() | acknowledged, insertedId (satu ObjectId) |
insertMany() | acknowledged, insertedCount, insertedIds (peta indeks → ObjectId) |
updateOne() / updateMany() | acknowledged, matchedCount, modifiedCount, upsertedCount |
deleteOne() / deleteMany() | acknowledged, deletedCount |
findOne() | satu document, atau null bila tidak ada yang cocok |
find() | FindCursor — dikonsumsi dengan toArray() atau iterasi |
acknowledged mencerminkan write concern: true berarti server sudah mengonfirmasi penulisan sebelum menjawab. Bedakan pula matchedCount (berapa document yang cocok dengan filter) dari modifiedCount (berapa yang benar-benar berubah) — nilai yang cocok belum tentu berubah bila isinya sudah sama.
Satu File, Satu Siklus CRUD #
File berikut menuntaskan seluruh siklus pada collection buku, dari insertMany sampai deleteMany, dengan setiap hasil dicetak:
// crud-toko-buku.js — operasi CRUD lengkap pada collection buku
// Jalankan: MONGODB_URI="mongodb://127.0.0.1:27017/?directConnection=true" node crud-toko-buku.js
const { MongoClient } = require("mongodb")
const uri = process.env.MONGODB_URI ?? "mongodb://127.0.0.1:27017/?directConnection=true"
async function main() {
const client = new MongoClient(uri)
await client.connect()
const buku = client.db("latihan_js").collection("buku")
await buku.drop().catch(() => {}) // reset agar contoh bisa diulang
// ===== CREATE =====
const r1 = await buku.insertMany([
{ judul: "Laskar Pelangi", penulis: "Andrea Hirata", harga: 95000, stok: 12, genre: "fiksi" },
{ judul: "Bumi Manusia", penulis: "Pramoedya A. Toer", harga: 110000, stok: 8, genre: "fiksi" },
{ judul: "Atomic Habits", penulis: "James Clear", harga: 125000, stok: 20, genre: "pengembangan diri" },
{ judul: "Filosofi Teras", penulis: "Henry Manampiring", harga: 90000, stok: 15, genre: "pengembangan diri" },
])
console.log("insertMany:", r1.acknowledged, "| insertedCount:", r1.insertedCount)
const r2 = await buku.insertOne({ judul: "Rumah Kaca", penulis: "Pramoedya A. Toer", harga: 105000, stok: 5, genre: "fiksi" })
console.log("insertOne :", r2.acknowledged, "| insertedId:", r2.insertedId)
// ===== READ =====
const semua = await buku.find({ genre: "fiksi" }).toArray()
console.log("find+toArray (genre fiksi):", semua.map(d => d.judul))
const satu = await buku.findOne({ judul: "Atomic Habits" }, { projection: { _id: 0, judul: 1, harga: 1 } })
console.log("findOne (Atomic Habits):", satu)
// ===== UPDATE =====
const r3 = await buku.updateOne({ judul: "Bumi Manusia" }, { $set: { harga: 120000 } })
console.log("updateOne :", "matched:", r3.matchedCount, "| modified:", r3.modifiedCount, "| upserted:", r3.upsertedCount)
const r4 = await buku.updateMany({ genre: "fiksi" }, { $inc: { stok: -1 } })
console.log("updateMany:", "matched:", r4.matchedCount, "| modified:", r4.modifiedCount, "| upserted:", r4.upsertedCount)
// ===== DELETE =====
const r5 = await buku.deleteOne({ judul: "Rumah Kaca" })
console.log("deleteOne :", "deletedCount:", r5.deletedCount)
const r6 = await buku.deleteMany({ stok: { $lt: 10 } })
console.log("deleteMany:", "deletedCount:", r6.deletedCount)
console.log("sisa buku :", await buku.countDocuments())
await client.close()
}
main().catch(err => {
console.error("GAGAL:", err.name, "-", err.message)
process.exit(1)
})MONGODB_URI="mongodb://127.0.0.1:27017/?directConnection=true" node crud-toko-buku.jsOutput yang Dihasilkan:
insertMany: true | insertedCount: 4
insertOne : true | insertedId: new ObjectId('6ad3eec08ea4d11914c5ba24')
find+toArray (genre fiksi): [ 'Laskar Pelangi', 'Bumi Manusia', 'Rumah Kaca' ]
findOne (Atomic Habits): { judul: 'Atomic Habits', harga: 125000 }
updateOne : matched: 1 | modified: 1 | upserted: 0
updateMany: matched: 3 | modified: 3 | upserted: 0
deleteOne : deletedCount: 1
deleteMany: deletedCount: 1
sisa buku : 3Mari kita bedah baris-baris kuncinya. buku.drop().catch(() => {}) menghapus collection dari run sebelumnya sehingga contoh selalu bisa diulang dengan hasil yang sama. insertMany() menerima array object biasa dan membalas insertedIds berbentuk peta '0', '1', … — indeks array asal menunjuk ObjectId masing-masing. find() mengembalikan FindCursor; toArray() memuat seluruh hasilnya ke memori sebagai array (cara lain mengonsumsi cursor dibahas di Chapter Cursor dan Pagination). findOne() di sini memakai opsi projection untuk memangkas field — pembahasan lengkapnya di Chapter Query dan Projection. Operator $set dan $inc pada updateOne/updateMany berperilaku sama persis dengan yang kita praktikkan di Section CRUD Operations lewat mongosh.
Perhatikan pula jalannya updateMany({ genre: "fiksi" }, ...): filter itu cocok dengan tiga judul (termasuk Rumah Kaca yang baru ditambahkan), dan deleteMany({ stok: { $lt: 10 } }) hanya menjangkau Bumi Manusia yang stoknya tersisa 7 setelah dikurangi — angka-angka pada output adalah konsekuensi operasi sebelumnya, bukan nilai acak.
_id Bukan String: Bekerja dengan ObjectId #
Driver otomatis mengisi _id untuk setiap document baru — nilainya object ObjectId dari paket bson yang ikut terpasang bersama driver. Tipenya bukan string, dan pembedaan ini terasa begitu _id berpindah media: disimpan di file, dikirim lewat HTTP, atau dicetak ke log semuanya menjadikannya string. File berikut membuktikan akibatnya saat string dipakai langsung sebagai filter:
// id-bukan-string.js — _id bertipe ObjectId; string harus dikonversi saat dipakai query
// Jalankan: MONGODB_URI="mongodb://127.0.0.1:27017/?directConnection=true" node id-bukan-string.js
const { MongoClient, ObjectId } = require("mongodb")
const uri = process.env.MONGODB_URI ?? "mongodb://127.0.0.1:27017/?directConnection=true"
async function main() {
const client = new MongoClient(uri)
await client.connect()
const buku = client.db("latihan_js").collection("buku")
await buku.drop().catch(() => {})
const hasil = await buku.insertOne({ judul: "Filosofi Teras", harga: 90000 })
const id = hasil.insertedId
const idString = id.toString()
console.log("constructor insertedId :", id.constructor.name)
console.log("id.toString() :", idString)
// Query memakai string mentah — tipe tidak cocok dengan ObjectId
const pakaiString = await buku.findOne({ _id: idString }, { projection: { judul: 1, _id: 0 } })
console.log("findOne(_id: string) :", pakaiString)
// Konversi string -> ObjectId sebelum dipakai sebagai filter
const pakaiObjectId = await buku.findOne({ _id: new ObjectId(idString) }, { projection: { judul: 1, _id: 0 } })
console.log("findOne(new ObjectId) :", pakaiObjectId)
// Waktu pembuatan ikut tersimpan di dalam ObjectId
console.log("getTimestamp() :", id.getTimestamp().toISOString())
await client.db("latihan_js").dropDatabase() // bersihkan latihan
await client.close()
}
main().catch(err => {
console.error("GAGAL:", err.name, "-", err.message)
process.exit(1)
})MONGODB_URI="mongodb://127.0.0.1:27017/?directConnection=true" node id-bukan-string.jsOutput yang Dihasilkan:
constructor insertedId : ObjectId
id.toString() : 6ad3ef03394559f9a781fe17
findOne(_id: string) : null
findOne(new ObjectId) : { judul: 'Filosofi Teras' }
getTimestamp() : 2026-10-17T21:56:19.000ZfindOne({ _id: idString }) mengembalikan null meskipun document itu ada — tidak ada pesan galat, karena string memang pola query yang sah; hanya saja tidak ada document _id bertipe string. Konversi new ObjectId(idString) memulihkan tipe aslinya dan query langsung menemukan document. Method getTimestamp() memperlihatkan data yang ikut terkandung di ObjectId: waktu pembuatannya.
Rangkuman #
- Semua operasi CRUD adalah method pada handle collection:
insertOne/insertMany,find/findOne,updateOne/updateMany,deleteOne/deleteMany. - Object hasil memuat
acknowledgedplus field spesifik:insertedIdvsinsertedIds,matchedCount/modifiedCount,deletedCount. find()mengembalikanFindCursor—toArray()memuat seluruh hasil ke memori sekaligus._iddari driver bertipeObjectId; ubah string kembali dengannew ObjectId(...)sebelum dipakai sebagai filter, dan baca waktu pembuatannya lewatgetTimestamp().