Aplikasi nyata jarang bekerja dengan satu berkas saja; data tersusun dalam folder — arsip per bulan, unggahan per pengguna, hasil per batch. Mengelola struktur itulah tugas operasi direktori: membuat folder, melihat isinya, menyalin dan memindahkan berkas, lalu membersihkan bekasnya. Semua operasi tersebut tersedia di node:fs/promises dengan nama yang mudah ditebak. Pada chapter ini kita rangkai kelima operasi utama menjadi satu skenario kerja utuh yang bisa dijalankan berulang.

Lima Operasi Utama #

Sebelum masuk ke contoh, mari petakan operasi yang akan kita pakai:

  • mkdir membuat direktori. Opsi { recursive: true } membuat folder induk sekaligus bila perlu, dan — bonus penting — tidak memunculkan error bila folder sudah ada, sehingga aman dijalankan berulang.
  • readdir membaca isi direktori. Dengan opsi { withFileTypes: true }, setiap entri menjadi object Dirent yang tahu jenisnya sendiri: berkas, direktori, atau symbolic link.
  • copyFile menduplikasi isi berkas ke path tujuan. Yang disalin hanya isinya; metadata seperti waktu pembuatan tidak ikut.
  • rename memindahkan berkas ke lokasi baru atau mengganti namanya. Operasi ini berlangsung utuh dalam satu langkah dan juga berlaku untuk direktori.
  • rm menghapus berkas maupun direktori. Opsi { recursive: true, force: true } menghapus folder beserta seluruh isinya dan tidak memunculkan error bila targetnya memang sudah tidak ada.

Kelimanya berasal dari tradisi POSIX yang diwarisi Node.js, jadi nama-namanya akan terasa akrab bagi yang pernah memakai perintah mkdir, cp, mv, dan rm di terminal.

Skenario Lengkap: Menata Ruang Kerja #

Contoh berikut memainkan kelima operasi dalam satu alur: menyiapkan folder kerja, menyalin berkas catatan.txt (tersedia di bundle chapter) ke dalamnya, memindahkan salinan itu ke subfolder arsip, melihat isi folder, lalu membersihkan semuanya.

js
import { mkdir, readdir, copyFile, rename, rm } from "node:fs/promises";

// recursive: true membuat folder induk sekaligus, aman dijalankan berulang
await mkdir("ruang-kerja/arsip", { recursive: true });
console.log("1. direktori ruang-kerja/arsip siap");

// copyFile: duplikasi isi berkas ke lokasi baru
await copyFile("catatan.txt", "ruang-kerja/draf.txt");
console.log("2. catatan.txt disalin menjadi ruang-kerja/draf.txt");

// rename: pindahkan sekaligus ganti nama
await rename("ruang-kerja/draf.txt", "ruang-kerja/arsip/laporan-2026.txt");
console.log("3. draf.txt dipindahkan ke ruang-kerja/arsip/laporan-2026.txt");

// salin satu lagi agar folder utama berisi berkas dan direktori
await copyFile("catatan.txt", "ruang-kerja/catatan-baru.txt");

// readdir dengan withFileTypes: daftar isi beserta jenisnya
const daftar = await readdir("ruang-kerja", { withFileTypes: true });
daftar.sort((a, b) => (a.name < b.name ? -1 : 1));
console.log("4. isi ruang-kerja:");
for (const entry of daftar) {
  const jenis = entry.isDirectory() ? "direktori" : "berkas";
  console.log(`   - ${entry.name} (${jenis})`);
}

// rm recursive + force: hapus ruang-kerja beserta seluruh isinya
await rm("ruang-kerja", { recursive: true, force: true });
console.log("5. ruang-kerja dan seluruh isinya dihapus");

Output yang Dihasilkan:

text
1. direktori ruang-kerja/arsip siap
2. catatan.txt disalin menjadi ruang-kerja/draf.txt
3. draf.txt dipindahkan ke ruang-kerja/arsip/laporan-2026.txt
4. isi ruang-kerja:
   - arsip (direktori)
   - catatan-baru.txt (berkas)
5. ruang-kerja dan seluruh isinya dihapus

Mari kita telaah bagian-bagian pentingnya:

  1. Baris 4 (mkdir recursive): satu panggilan membuat dua tingkat folder sekaligus — ruang-kerja dan arsip di dalamnya. Eksekusi kedua kalinya tidak error karena recursive: true menganggap “sudah ada” sebagai keberhasilan.
  2. Baris 8 dan 16 (copyFile): sumber tetap utuh; hasil salinan berdiri sendiri di path tujuan. Path tujuan memakai garis miring / — di Node.js, path bergaya ini bekerja di semua sistem operasi termasuk Windows.
  3. Baris 12 (rename): satu operasi sekaligus memindahkan lokasi dan mengganti nama — draf.txt menjadi arsip/laporan-2026.txt. Tidak ada dua langkah “salin lalu hapus” di sini.
  4. Baris 19-25 (readdir + Dirent): setiap entry memiliki properti name dan method pemeriksa jenis seperti isDirectory(). Dibanding menebak jenis dari ekstensi nama, Dirent membaca kebenaran dari sistem operasi.
  5. Baris 28 (rm recursive + force): satu panggilan membersihkan ruang-kerja beserta arsip dan semua berkasnya. force: true membuat skrip tetap berjalan mulus walau folder sudah tidak ada — bagian dari jaminan bahwa contoh ini dapat diulang.

Urutan readdir Tidak Dijamin #

Satu detail pada contoh di atas perlu ditegaskan: baris 20 memanggil daftar.sort(...) sebelum mencetak. Alasannya bukan gaya — readdir mengembalikan entri dalam urutan yang disediakan sistem operasi, dan urutan tersebut bervariasi antar platform bahkan antar kondisi disk. Menyortir secara eksplisit membuat output program — dan test yang memeriksanya — stabil di mana pun kode dijalankan. Untuk daftar yang ditampilkan manusia, urutan alfabetis juga hampir selalu lebih nyaman dibaca.

Untuk mengganti nama berkas yang sedang dipakai proses lain, rename tetap aman di Linux dan macOS karena operasinya atomik; di Windows, berkas yang terkunci proses lain dapat menolak diganti. Rujukan resmi masing-masing operasi: mkdir , copyFile , rename , dan rm .

Struktur folder rapi, operasi lancar — namun sering kali kita hanya butuh informasi tentang berkas: seberapa besar, kapan terakhir diubah, berkas atau direktori. Chapter Metadata Berkas membahas stat dan lstat untuk kebutuhan itu.