Driver Node.js

Menghubungkan aplikasi Node.js ke MongoDB dan menjalankan CRUD dari kode.

Daftar isi

mongosh mengeksekusi setiap perintah secara manual, satu kali untuk setiap pengetikan. Aplikasi nyata menjalankan operasi yang sama ribuan kali sehari dari dalam kode melalui driver. Driver adalah pustaka yang menerjemahkan fungsi bahasa pemrograman menjadi perintah MongoDB lalu mengirimkannya ke server. Topik ini membahas driver resmi Node.js, mulai dari pemasangan package, pembuatan koneksi, siklus CRUD lengkap pada koleksi pekerjaan di basis data tugas, sampai padanan setiap operasi mongosh pada kode. Setelah mengikuti topik ini, aplikasi Node.js dapat membaca dan menulis MongoDB tanpa campur tangan shell.

Instalasi driver dan connection string#

Driver resmi Node.js dipublikasikan sebagai package mongodb. Pada proyek sungguhan, package dipasang dari npm dengan npm install mongodb setelah npm init menyiapkan berkas package.json. Lingkungan latihan jalur ini sudah menyediakan package tersebut pada folder tersendiri, sehingga pemasangan ulang tidak diperlukan, dan program cukup diarahkan ke folder itu melalui variabel NODE_PATH pada saat dijalankan. Pola ini dipakai oleh seluruh contoh Node.js pada modul ini.

Koneksi dibangun oleh kelas MongoClient dari package mongodb. Konstruktor menerima connection string, yaitu alamat server berformat mongodb://host:port, misalnya mongodb://127.0.0.1:27017 untuk server lokal tanpa autentikasi. Satu objek klien memelihara kumpulan koneksi ke server dan sebaiknya dibuat satu kali untuk seluruh proses aplikasi. Metode connect() menghubungi server secara asinkron, sedangkan client.db() dan db.collection() hanya menunjuk nama basis data dan koleksi tanpa mengirim apa pun ke jaringan. Koleksi yang belum ada boleh ditunjuk, karena MongoDB menyediakannya pada saat dokumen pertama disisipkan. Connection string juga dapat membawa opsi tambahan sebagai parameter query, misalnya batas waktu pemilihan server atau nama replica set, yaitu kelompok server MongoDB yang menyimpan salinan data yang sama. Opsi semacam itu berlaku untuk seluruh operasi klien, sehingga pengaturan seperti serverSelectionTimeoutMS cukup ditetapkan sekali pada alamatnya.

Menjalankan CRUD dari aplikasi#

Program aplikasi.js menjalankan satu siklus CRUD lengkap pada koleksi pekerjaan, yaitu daftar pekerjaan pribadi dengan field judul, kategori, prioritas, dan selesai. Koleksi di-reset terlebih dahulu agar keluaran program selalu sama, lalu dokumen disisipkan, dibaca, diperbarui, dan dihapus di dalam satu fungsi main asinkron. Setiap metode driver mengembalikan Promise, sehingga setiap operasi harus menunggu dengan await sebelum hasilnya dipakai.

js aplikasi.js
// CRUD pekerjaan pribadi dari aplikasi Node.js
const { MongoClient } = require("mongodb");

const client = new MongoClient("mongodb://127.0.0.1:27017");

async function main() {
  await client.connect();
  const koleksi = client.db("tugas").collection("pekerjaan");

  // koleksi di-reset agar keluaran program selalu sama
  await koleksi.deleteMany({});

  // CREATE: menyisipkan banyak dokumen sekaligus
  const seed = await koleksi.insertMany([
    { judul: "Belanja mingguan", kategori: "rumah", prioritas: 2, selesai: false },
    { judul: "Kirim dokumen pajak", kategori: "pribadi", prioritas: 1, selesai: false },
    { judul: "Ganti filter air", kategori: "rumah", prioritas: 3, selesai: false },
    { judul: "Baca artikel basis data", kategori: "belajar", prioritas: 5, selesai: true }
  ]);
  console.log("Dokumen baru:", seed.insertedCount);

  // CREATE: menyisipkan satu dokumen tambahan
  await koleksi.insertOne({ judul: "Rapikan arsip foto", kategori: "pribadi", prioritas: 4, selesai: false });

  // READ: membaca satu dokumen pertama yang cocok dengan filter
  const pajak = await koleksi.findOne({ kategori: "pribadi" }, { sort: { prioritas: 1 } });
  console.log("Pertama pribadi:", pajak.judul);

  // READ: membaca seluruh dokumen dengan proyeksi
  const semua = await koleksi.find({}, { projection: { _id: 0, judul: 1, selesai: 1 } }).sort({ prioritas: 1 }).toArray();
  for (const d of semua) {
    console.log("[" + (d.selesai ? "x" : " ") + "] " + d.judul);
  }

  // UPDATE: menandai satu pekerjaan selesai
  const ubah = await koleksi.updateOne({ judul: "Belanja mingguan" }, { $set: { selesai: true } });
  console.log("Dokumen diubah:", ubah.modifiedCount);

  // DELETE: menghapus satu pekerjaan yang tidak dibutuhkan lagi
  const hapus = await koleksi.deleteOne({ judul: "Ganti filter air" });
  console.log("Dokumen dihapus:", hapus.deletedCount);
  console.log("Sisa dokumen:", await koleksi.countDocuments());

  await client.close();
}

main().catch((e) => { console.error(e); process.exitCode = 1; });
text Keluaran
Dokumen baru: 4
Pertama pribadi: Kirim dokumen pajak
[ ] Kirim dokumen pajak
[ ] Belanja mingguan
[ ] Ganti filter air
[ ] Rapikan arsip foto
[x] Baca artikel basis data
Dokumen diubah: 1
Dokumen dihapus: 1
Sisa dokumen: 4

Lima baris daftar pada keluaran berasal dari operasi READ. Metode findOne dengan opsi sort mengembalikan pekerjaan pribadi berprioritas tertinggi, sedangkan find dengan proyeksi menyembunyikan field _id yang nilainya dihasilkan server. Baris Dokumen diubah dan Dokumen dihapus mencatat hasil updateOne dan deleteOne berupa jumlah dokumen yang tersentuh. Angka modifiedCount dan deletedCount memberi kepastian bahwa operasi menyasar dokumen yang dimaksud, sesuatu yang tidak dilaporkan mongosh secara eksplisit.

Program dijalankan dari folder topik. Variabel NODE_PATH ditetapkan terlebih dahulu agar package mongodb pada folder latihan dapat ditemukan, kemudian berkasnya dieksekusi dengan node.

bash
# menetapkan lokasi package driver, lalu menjalankan program Node.js
export NODE_PATH=/home/kal/tools/node-driver/node_modules
node aplikasi.js
text Keluaran
Dokumen baru: 4
Pertama pribadi: Kirim dokumen pajak
[ ] Kirim dokumen pajak
[ ] Belanja mingguan
[ ] Ganti filter air
[ ] Rapikan arsip foto
[x] Baca artikel basis data
Dokumen diubah: 1
Dokumen dihapus: 1
Sisa dokumen: 4

Keluaran eksekusi bash berisi baris yang sama dengan blok sebelumnya karena programnya sama dan datanya selalu ditanam ulang dari awal. Program aman dijalankan berulang kali, sehingga keluaran ini dapat dipakai sebagai acuan saat mencoba variasi kode.

Memeriksa hasil dari mongosh#

Data yang ditulis aplikasi adalah data biasa di sisi server, sehingga dapat diperiksa dari mongosh kapan saja. Pemeriksaan semacam ini mempercepat pencarian penyebab masalah, karena kesalahan kode dan kesalahan data dapat dipisahkan dalam satu langkah. Blok berikut menyusun ulang kondisi akhir koleksi pekerjaan yang ditinggalkan program, yaitu pekerjaan yang telah ditandai selesai dan Ganti filter air yang telah dihapus, kemudian membaca pekerjaan yang belum selesai. Hasil insertMany ditampung pada variabel hasil, tetapi hanya jumlah dokumen yang dicetak agar keluarannya tetap ringkas. Tampilan array berindentasi dengan tanda kutip tunggal adalah cara shell merender hasil kursor, dan cara itu tidak mengubah isi datanya.

mongosh
use tugas
const hasil = db.pekerjaan.insertMany([
  { judul: "Kirim dokumen pajak", kategori: "pribadi", prioritas: 1, selesai: false },
  { judul: "Belanja mingguan", kategori: "rumah", prioritas: 2, selesai: true },
  { judul: "Rapikan arsip foto", kategori: "pribadi", prioritas: 4, selesai: false },
  { judul: "Baca artikel basis data", kategori: "belajar", prioritas: 5, selesai: true }
])
print("Dokumen tersimpan: " + db.pekerjaan.countDocuments())
db.pekerjaan.find({ selesai: false }, { _id: 0, judul: 1, prioritas: 1 }).sort({ prioritas: 1 })
text Keluaran
switched to db tugas

Dokumen tersimpan: 4

[
  { judul: 'Kirim dokumen pajak', prioritas: 1 },
  { judul: 'Rapikan arsip foto', prioritas: 4 }
]

Keluaran menampilkan dua dokumen dengan proyeksi terbatas pada judul dan prioritas, sedangkan field _id disembunyikan karena tidak berarti bagi pembacaan manual. Angka pada Dokumen tersimpan berasal dari countDocuments, sehingga jumlahnya dibaca ulang dari server dan bukan dihitung dari tampilan. Apabila program aplikasi dijalankan lebih dahulu pada lingkungan yang sama, perintah db.pekerjaan.find() menampilkan isi yang sama tanpa perlu menanam data ulang. Kebiasaan memverifikasi hasil tulisan dari shell melengkapi siklus pengembangan, yaitu kode menulis dan shell memeriksa.

Padanan operasi mongosh pada driver#

Setiap metode driver memanggil perintah server yang sama dengan perintah mongosh, sehingga pemetaan antar keduanya berlangsung hampir satu banding satu. Perbedaan yang perlu diadaptasi ada pada bentuk, bukan pada makna. Eksekusi yang di shell berlangsung seketika per baris menjadi asinkron di kode. Proyeksi yang di shell menjadi argumen kedua langsung berpindah ke dalam objek opsi. Hasil tulisan yang di shell dicetak sebagai ringkasan menjadi objek hasil dengan angka tersendiri. Tabel berikut merangkum perbedaan yang paling sering ditemui.

AspekmongoshDriver Node.js
sifat eksekusilangsung per barisasinkron, menunggu await
proyeksi pada findargumen kedua langsungbagian projection dari objek opsi
urutan pada findOneargumen sort pada metodebagian sort dari objek opsi
hasil tulisanringkasan cetakobjek hasil dengan insertedCount, modifiedCount, deletedCount
hasil pembacaankursor yang dicetak otomatiskursor yang diiterasi atau toArray()

Filter dan operator update tidak masuk tabel karena tidak berubah sama sekali. Dokumen { $set: { selesai: true } } yang diketik di mongosh dipakai apa adanya sebagai argumen updateOne pada driver, dan begitu pula seluruh operator query lain. Nama metodenya juga mengikuti pola yang sama, yaitu insertMany, findOne, updateOne, dan deleteOne merujuk langsung pada perintah servernya. Konsistensi ini membuat pengetahuan shell menjadi modal langsung untuk kode, dan sebaliknya, operasi yang diragukan dari kode dapat diuji ulang di mongosh dengan perintah yang setara.

Kesalahan umum#

Kesalahan pertama adalah memanggil metode driver tanpa await. Metode tersebut mengembalikan Promise, sehingga kode yang langsung membaca hasilnya menerima objek Promise, bukan dokumen. Kesalahan ini tidak menghentikan program dengan error apa pun. Gejalanya baru terlihat saat field yang dibaca bernilai undefined, atau berupa TypeError pada baris yang mencoba membaca field dari objek Promise tersebut. Perbaikannya adalah menunggu setiap operasi database dengan await di dalam fungsi async.

Kesalahan kedua adalah mencocokkan _id dengan tipe yang salah. Filter { _id: "101" } tidak pernah cocok dengan dokumen yang _id-nya bertipe angka, karena pencocokan MongoDB mempertimbangkan tipe BSON di samping nilainya. Kondisi sebaliknya berlaku sama, yaitu string heksadesimal ObjectId tidak cocok dengan _id bertipe ObjectId sebelum dikonversi dengan kelas ObjectId dari package.

Kesalahan ketiga adalah membuat MongoClient baru untuk setiap operasi. Setiap klien memelihara kumpulan koneksi sendiri, sehingga pembuatan klien berulang menumpuk koneksi dan menambah waktu jabat tangan jaringan. Klien sebaiknya dibuat satu kali pada saat aplikasi dimulai, dipakai bersama oleh seluruh modul, dan ditutup dengan client.close() hanya ketika proses benar-benar berakhir.

Ringkasan#

  • Driver Node.js mengakses MongoDB melalui satu objek MongoClient yang dibuat dari connection string dan dipakai bersama oleh seluruh proses aplikasi.
  • Metode insertMany, findOne, find, updateOne, dan deleteOne menjalankan perintah server yang sama dengan mongosh, dengan filter dan operator yang tidak berubah.
  • Setiap metode mengembalikan Promise, sehingga operasi harus menunggu dengan await dan hasil tulisannya dibaca dari insertedCount, modifiedCount, atau deletedCount.
  • Proyeksi dan urutan menyatu ke dalam objek opsi, sedangkan hasil kursor diiterasi dengan for await atau dimuat dengan toArray().

Referensi#