Sebagian besar aplikasi nyata tidak berhenti di memori: konfigurasi dibaca dari berkas, data pengguna disimpan ke berkas, dan aktivitas dicatat ke berkas log. Node.js menyediakan module fs — singkatan dari file system — sebagai pintu resmi menuju semua operasi tersebut. Pada chapter ini kita memetakan bentuk-bentuk API yang ditawarkan module fs, memahami perbedaan perilakunya lewat contoh yang dapat dijalankan, dan belajar memilih gaya yang tepat untuk setiap situasi.

Module Inti dengan Prefix node #

fs adalah core module: module bawaan Node.js yang sudah tersedia begitu runtime terpasang, tanpa perlu npm install dan tanpa entri di package.json. Untuk memakainya, kita cukup menulis import ... from "node:fs". Prefix node: menjadi konvensi pada Course ini karena dua alasan. Pertama, prefix tersebut memastikan kita mendapat module inti yang sebenarnya, bukan package npm lain yang kebetulan bernama fs. Kedua, prefix tersebut membuat asal import terlihat eksplisit: pembaca kode langsung tahu bahwa yang diminta adalah fasilitas runtime, bukan dependensi eksternal.

Perlu diingat bahwa module fs hanya tersedia di lingkungan Node.js. Browser sengaja tidak memberikan JavaScript akses langsung ke berkas di komputer pengguna demi isolasi dan keamanan; halaman web hanya memperoleh mekanisme terbatas seperti pemilih unggah berkas dan penyimpanan per-origin. Di Node.js, kode berjalan dengan hak akses proses yang menjalankannya, sehingga operasi baca-tulis berkas dapat dilakukan dengan leluasa — sekaligus menuntut kita memeriksa kembali setiap path yang dituju.

Tiga Gaya API untuk Satu Fasilitas #

Module fs menyediakan hampir semua operasi dalam tiga bentuk pembungkus. Operasinya sama persis — membaca catatan.txt tetaplah membaca catatan.txt — yang berbeda hanya cara kode kita menerima hasilnya. Tabel berikut merangkum ketiga gaya tersebut.

GayaSumber importCara menerima hasilPosisi hari ini
Promisenode:fs/promisesawait pada promise yang dikembalikanPilihan utama untuk kode baru
Callbacknode:fsFunction argumen terakhir (error-first)Kode lama dan API berbasis event
Synchronousnode:fs (nama berakhiran Sync)Nilai kembali langsung, atau throwSkrip singkat dan proses awal program

Contoh berikut membaca berkas catatan.txt — sudah disertakan di bundle chapter ini — dengan ketiga gaya secara berurutan. Simpan dan jalankan dengan node tiga-gaya-api.mjs.

js
import { readFile } from "node:fs";
import { readFile as baca } from "node:fs/promises";
import { readFileSync } from "node:fs";

// Gaya 1: promise (rekomendasi untuk kode baru)
const isiPromise = await baca("catatan.txt", "utf8");
console.log(`promise  : ${isiPromise.trim()}`);

// Gaya 2: synchronous (blokir sampai selesai)
const isiSync = readFileSync("catatan.txt", "utf8");
console.log(`sync     : ${isiSync.trim()}`);

// Gaya 3: callback (err-first, gaya lama)
readFile("catatan.txt", "utf8", (err, data) => {
  if (err) {
    console.error(`gagal: ${err.message}`);
    return;
  }
  console.log(`callback : ${data.trim()}`);
});

Output yang Dihasilkan:

text
promise  : Belajar module fs di Node.js.
Materi: membaca berkas, menulis berkas, direktori.
sync     : Belajar module fs di Node.js.
Materi: membaca berkas, menulis berkas, direktori.
callback : Belajar module fs di Node.js.
Materi: membaca berkas, menulis berkas, direktori.

Mari kita telaah baris-baris kode di atas:

  1. Baris 1-3 (import): tiga import diambil dari dua sumber. readFile versi callback dan readFileSync berada di node:fs, sedangkan versi promise berada di node:fs/promises. Alias readFile as baca diperlukan karena dua binding tidak boleh memakai nama yang sama dalam satu berkas.
  2. Baris 6-7 (gaya promise): await baca(...) menunggu operasi selesai, lalu mengembalikan isi berkas sebagai string karena encoding "utf8" ikut disertakan.
  3. Baris 10-11 (gaya sync): readFileSync mengembalikan string secara langsung tanpa await; baris berikutnya tidak berjalan sebelum pembacaan selesai.
  4. Baris 14-20 (gaya callback): function terakhir menerima dua parameter — err dan data. Pola ini disebut error-first callback: err bernilai null saat sukses. Hasil gaya callback muncul paling akhir di output karena operasinya asynchronous: penyelesaiannya dijadwalkan setelah baris-baris lain selesai.

Sync Menahan, Async Melepas #

Perbedaan paling penting dari ketiga gaya adalah pertanyaan siapa yang menunggu. Versi Sync menahan eksekusi kode berikutnya sampai operasi berkas selesai. Versi promise dan callback melepaskan eksekusi: baris berikutnya langsung berjalan, dan hasil operasi diterima belakangan melalui event loop. Contoh berikut membuat perbedaan itu terlihat dari urutan cetakan angka.

js
import { readFile } from "node:fs";
import { readFileSync } from "node:fs";

console.log("1: program dimulai");

const isi = readFileSync("catatan.txt", "utf8");
console.log(`2: sync selesai, panjang isi ${isi.length} karakter`);

readFile("catatan.txt", "utf8", (err, data) => {
  if (err) throw err;
  console.log(`3: callback selesai, panjang isi ${data.length} karakter`);
});

console.log("4: baris terakhir program");

Output yang Dihasilkan:

text
1: program dimulai
2: sync selesai, panjang isi 81 karakter
4: baris terakhir program
3: callback selesai, panjang isi 81 karakter

Angka 3 tercetak setelah angka 4, padahal pemanggilan readFile berada di atas console.log terakhir. Penjelasannya: readFileSync pada baris 6 menahan program sampai selesai (angka 2 langsung menyusul angka 1), sementara readFile versi callback hanya mendaftarkan pekerjaan lalu langsung menyerahkan kendali (angka 4 berjalan dulu). Setelah semua baris selesai dan operasi berkas beres, event loop memanggil callback kita dengan hasilnya (angka 3). Mekanisme penjadwalan ini dibahas tuntas pada Section Asynchronous Runtime.

Memilih Gaya untuk Kode Kita #

Pedoman praktisnya ringkas saja:

  • Default: node:fs/promises dengan await. Kode mudah dibaca berurutan, error ditangani dengan try...catch, dan event loop tetap bebas.
  • Sync untuk skrip sekali jalan. Tooling, migrasi data, atau pembacaan konfigurasi awal cocok memakai readFileSync karena kesederhanaannya dan karena tidak ada permintaan lain yang perlu dilayani.
  • Callback untuk kode warisan. Banyak proyek lama dan sebagian API berbasis event masih memakai pola error-first; kita perlu bisa membacanya meski jarang menulisnya.

Bentuk import lain juga sering dijumpai di proyek nyata:

js Bentuk import lain yang juga valid
import fs from "node:fs";

// Default export menampung API callback + sync,
// ditambah ruang nama fs.promises untuk versi promise
const isi = await fs.promises.readFile("catatan.txt", "utf8");

Bentuk import fs from "node:fs" lalu fs.promises.readFile berguna ketika satu berkas memakai banyak API sekaligus. Untuk kode baru yang hanya memakai beberapa operasi, named import dari node:fs/promises lebih ringkas dan membuat asal setiap function terlihat jelas. Rujukan lengkap ketiga gaya API tersedia di dokumentasi resmi: halaman File system memuat API promise , API callback , dan API synchronous .

Dengan peta gaya API di tangan, kita siap masuk ke operasi yang paling sering dipakai sehari-hari: membaca isi berkas, baik sebagai teks maupun sebagai data biner mentah — materi Chapter Membaca Berkas.