Aplikasi web melayani permintaan satu per satu dalam jumlah besar — dan setiap permintaan yang butuh data tidak boleh membangun koneksi MongoDB dari nol. Jabatan tangan TCP tiga arah, negosiasi TLS, sampai penemuan topologi replica set semuanya butuh waktu. Driver mongodb menjawabnya dengan connection pool: sekali dibuka, sekumpulan koneksi tinggal dipakai ulang. Pada bagian ini kita menyiapkan fondasi proyek — struktur folder, dependensi, dan modul db.js yang menjadi satu-satunya pintu menuju database.

Struktur Proyek #

Buat folder proyek dan instal satu-satunya dependensi runtime kita:

bash
mkdir api-manajemen-tugas && cd api-manajemen-tugas
npm init -y
npm install mongodb@7

Struktur akhir proyek setelah seluruh bagian selesai:

text
api-manajemen-tugas/
├── src/
│   ├── db.js          ← bagian ini: koneksi + pool
│   ├── tugas.js       ← Bagian 2: repository tugas
│   ├── validasi.js    ← Bagian 3: skema + pemeriksaan payload
│   ├── users.js       ← Bagian 4: pengguna + API key
│   └── server.js      ← Bagian 2-4: router HTTP
└── tests/
    └── api-tes.js     ← suite tes mandiri

Dua aturan arsitektur yang kita pegang: satu modul satu tanggung jawab, dan seluruh akses database lewat modul db.js — tidak ada handler yang membuat MongoClient sendiri, sebagaimana pola yang diperkenalkan chapter Struktur Aplikasi Node.js.

Modul Koneksi #

js
// db.js — koneksi singleton + connection pool
// Bisa dijalankan langsung untuk menguji koneksi:
//   node src/db.js
// Variabel lingkungan (opsional):
//   MONGODB_URI  connection string (default: server lokal rs0)
//   DB_NAME      nama database      (default: proyek_api_tugas)

const { MongoClient } = require("mongodb");

const URI = process.env.MONGODB_URI ||
  "mongodb://127.0.0.1:27017/?directConnection=true";
const NAMA_DB = process.env.DB_NAME || "proyek_api_tugas";

let klien = null;
let db = null;

// Satu pool koneksi untuk seluruh proses — fungsi ini mengembalikan
// handle database yang sama berapa kali pun dipanggil.
async function ambilDb() {
  if (db) return db;
  klien = new MongoClient(URI, { maxPoolSize: 10 });
  await klien.connect();
  db = klien.db(NAMA_DB);
  return db;
}

// Menutup pool dengan rapi saat proses berhenti.
async function tutupKoneksi() {
  if (klien) await klien.close();
  klien = null;
  db = null;
}

// Cek kesehatan koneksi dengan command ping.
async function ping() {
  const d = await ambilDb();
  return d.command({ ping: 1 });
}

if (require.main === module) {
  ping()
    .then(function (hasil) {
      console.log("ping ok:", hasil.ok, "| database:", NAMA_DB);
      return tutupKoneksi();
    })
    .catch(function (galat) {
      console.error("koneksi gagal:", galat.message);
      process.exit(1);
    });
}

module.exports = { ambilDb, tutupKoneksi, ping, URI, NAMA_DB };

Uji koneksi sebelum menulis hal lain:

bash
node src/db.js

Output yang Dihasilkan:

text
ping ok: 1 | database: proyek_api_tugas

Bedah Modul Baris-per-Baris #

  1. const URI = process.env.MONGODB_URI || ... — connection string dibaca dari variabel lingkungan dengan fallback ke server lokal Course. Praktik ini mengikuti chapter Environment Configuration: kode aplikasi tidak boleh menuliskan alamat server secara tetap agar satu kode yang sama berjalan di laptop dan di server produksi.
  2. let db = null + if (db) return db — pola singleton. Panggilan ambilDb() pertama membuat MongoClient dan menyambung; panggilan berikutnya menerima handle yang sama tanpa membuka pool baru. Tanpa penjaga ini, setiap handler akan membuka pool masing-masing dan jumlah koneksi ke server tumbuh tanpa kendali.
  3. new MongoClient(URI, { maxPoolSize: 10 }) — pool dibatasi 10 koneksi simultan, cukup untuk API satu proses. Nilai bawaan driver adalah 100; angka lebih kecil melindungi server bersama ketika beberapa aplikasi menumpuk di satu cluster.
  4. ping() — menjalankan command ping di database aktif, cara tercepat memastikan seluruh rantai (DNS, TCP, autentikasi, pemilihan node) benar-benar hidup.
  5. if (require.main === module) — blok ini hanya berjalan saat berkas dieksekusi langsung (node src/db.js), tidak saat di-require oleh modul lain. Satu berkas, dua peran: modul dan alat uji.

Konfigurasi Tanpa Kode #

Dua variabel lingkungan cukup untuk mengarahkan ulang seluruh aplikasi. Simpan berkas .env di folder proyek lalu jalankan dengan opsi --env-file bawaan Node.js — tanpa pustaka tambahan:

text .env untuk lingkungan lokal
MONGODB_URI=mongodb://127.0.0.1:27017/?directConnection=true
DB_NAME=proyek_api_tugas
bash
node --env-file=.env src/db.js
DB_NAME=api_uji node src/db.js

Output yang Dihasilkan:

text
ping ok: 1 | database: proyek_api_tugas
ping ok: 1 | database: api_uji

Perintah pertama membaca .env; perintah kedua membuktikan variabel biasa menimpa fallback tanpa mengubah satu baris kode. Sebagai pembanding, saat server mati driver mencoba menyambung berulang-ulang selama masa tunggu pemilihan server sebelum menyerah — sesuai siklus hidup koneksi pada chapter Connection Lifecycle:

bash
MONGODB_URI="mongodb://127.0.0.1:27099/" node src/db.js

Output yang Dihasilkan:

text
koneksi gagal: connect ECONNREFUSED 127.0.0.1:27099

Apa Berikutnya #

Pintu ke database sudah terbuka dan bisa diping kapan pun. Bagian 2 membangun lapisan di atasnya: repository tugas.js yang merangkum seluruh operasi data, lalu server.js yang mengekspos operasi tersebut sebagai endpoint HTTP.