Setelah bisa membaca, kebutuhan berikutnya adalah menyimpan: laporan yang dihasilkan program, pengaturan yang dipilih pengguna, sampai catatan log yang terus bertambah. Node.js menyediakan writeFile dan appendFile dari node:fs/promises untuk semua kebutuhan itu, dengan satu opsi kecil bernama flag yang mengubah cara data diletakkan ke dalam berkas. Pada chapter ini kita akan mencoba ketiga pola penulisan yang paling sering dipakai: menimpa isi, menambah isi, dan menulis JSON yang rapi.
Menimpa Isi dengan writeFile #
writeFile menulis data ke berkas dengan perilaku bawaan yang penting dipahami: berkas dibuat bila belum ada, dan isi lama diganti seluruhnya setiap kali penulisan dijalankan. Perilaku timpa ini berasal dari flag bawaan "w" (write), yang memotong berkas menjadi kosong sebelum menulis.
import { writeFile, readFile, unlink } from "node:fs/promises";
// Penulisan pertama: berkas dibuat bila belum ada
await writeFile("laporan.txt", "Laporan versi 1\n");
// Penulisan kedua: isi lama diganti seluruhnya (flag "w" default)
await writeFile("laporan.txt", "Laporan versi 2\n");
const isi = await readFile("laporan.txt", "utf8");
console.log(`isi akhir: ${isi.trim()}`);
// Bersih-bersih agar contoh bisa dijalankan ulang
await unlink("laporan.txt");
console.log("laporan.txt dihapus");Output yang Dihasilkan:
isi akhir: Laporan versi 2
laporan.txt dihapusMari kita telaah baris-baris kode di atas:
- Baris 4 dan 7 (dua kali
writeFile): penulisan pertama membuatlaporan.txt; penulisan kedua pada path yang sama menimpa isinya. Isi akhir hanya memuat versi 2 — bukti bahwawriteFiletidak menambah, melainkan mengganti. - Baris 9-10 (
readFileuntuk verifikasi): berkas dibaca kembali untuk memastikan apa yang benar-benar tersimpan di disk, bukan sekadar apa yang menurut kita ditulis. - Baris 13-14 (
unlink): berkas uji dihapus di akhir skrip. Dengan begitu contoh ini bersifat idempoten — dapat dijalankan berulang dengan hasil yang sama persis, karena setiap eksekusi selalu mulai dari kondisi bersih.
Perilaku timpa ini justru sering menjadi alasan memilih writeFile: laporan periodik, berkas cache, dan hasil ekspor umumnya memang ingin selalu berisi versi terbaru, bukan tumpukan versi lama.
Menambah Isi dengan appendFile dan Flag a #
Cerita berbeda berlaku untuk berkas log: kita tidak ingin isi lama hilang, melainkan baris baru ditempel di akhir. Untuk ini ada appendFile, atau writeFile dengan opsi { flag: "a" } (append). Keduanya menulis mulai dari posisi akhir berkas tanpa perlu membaca isi yang sudah ada.
import { writeFile, appendFile, readFile, unlink } from "node:fs/promises";
// Mulai dari isi bersih setiap kali contoh dijalankan
await writeFile("log.txt", "Senin: belajar writeFile\n");
// appendFile menambah baris di akhir tanpa membaca isi lama
await appendFile("log.txt", "Selasa: belajar appendFile\n");
await appendFile("log.txt", "Rabu: belajar flag \"a\"\n");
const isi = await readFile("log.txt", "utf8");
console.log(isi.trimEnd());
await unlink("log.txt");
console.log("log.txt dihapus");Output yang Dihasilkan:
Senin: belajar writeFile
Selasa: belajar appendFile
Rabu: belajar flag "a"
log.txt dihapusPerhatikan urutannya: baris 4 memakai writeFile biasa untuk me-reset isi log.txt menjadi satu baris, sehingga contoh selalu mulai dari keadaan yang sama meski dijalankan berulang. Setelah itu dua pemanggilan appendFile menempel baris baru di akhir tanpa menyentuh baris sebelumnya. Tanpa langkah reset di awal, isi lama dari eksekusi sebelumnya akan ikut terbaca dan hasil contoh berubah-ubah — jebakan klasik saat menulis skrip yang menyimpan status.
Flag yang paling sering dipakai dirangkum berikut ini; daftar lengkapnya ada di dokumentasi flag berkas .
| Flag | Perilaku |
|---|---|
"w" | Membuat berkas bila belum ada, lalu memotong isi lama menjadi kosong sebelum menulis (default writeFile) |
"a" | Melanjutkan penulisan dari akhir berkas; berkas dibuat bila belum ada |
"wx" | Seperti "w", tetapi gagal bila berkas sudah ada — penjaga agar tidak menimpa data penting secara tak sengaja |
Menulis JSON yang Rapi #
Data terstruktur paling enak disimpan sebagai JSON. Kebiasaan yang perlu kita bawa dari awal: selalu terjemahkan object dengan JSON.stringify
memakai indentasi, agar berkas tetap dapat dibaca manusia saat diperiksa.
import { writeFile, readFile, unlink } from "node:fs/promises";
const pengaturan = {
tema: "gelap",
bahasa: "id",
ukuranFont: 14,
};
// JSON.stringify dengan indentasi 2 spasi agar mudah dibaca manusia
const teks = JSON.stringify(pengaturan, null, 2) + "\n";
await writeFile("pengaturan.json", teks);
const hasil = await readFile("pengaturan.json", "utf8");
console.log(hasil.trimEnd());
await unlink("pengaturan.json");
console.log("pengaturan.json dihapus");Output yang Dihasilkan:
{
"tema": "gelap",
"bahasa": "id",
"ukuranFont": 14
}
pengaturan.json dihapusArgumen kedua JSON.stringify bernilai null karena kita tidak memakai replacer; argumen ketiga 2 meminta indentasi dua spasi per tingkat. Tanpa argumen ketiga, seluruh object akan ditulis sebagai satu baris panjang — hemat tempat, tetapi sulit diperiksa mata saat terjadi masalah. Baris baru "\n" di akhir adalah konvensi kecil yang membuat berkas ramah terhadap perintah terminal seperti cat.
Penulisan yang Lebih Aman #
Pola tulis-aman tersebut memanfaatkan sifat rename yang mengganti berkas secara utuh dalam satu langkah:
import { writeFile, rename } from "node:fs/promises";
await writeFile("pengaturan.tmp.json", teks);
await rename("pengaturan.tmp.json", "pengaturan.json");Pembaca selalu melihat pengaturan.json dalam keadaan utuh — versi lama atau versi baru, tidak pernah campuran — karena pergantian nama terjadi sebagai satu operasi. Operasi rename sendiri dibahas tuntas pada Chapter Direktori dan Operasi Berkas.
Kita kini bisa membaca dan menulis berkas tunggal. Aplikasi nyata tentu mengelola banyak berkas yang tersebar dalam folder; mengelola direktori, menyalin, memindahkan, dan menghapus adalah materi Chapter Direktori dan Operasi Berkas.