File System Operations (fs Module)

📖 Concept

The fs module is Node.js's interface to the operating system's file system. It provides methods to read, write, delete, rename, and watch files and directories.

Three API styles:

Style Example Use Case
Callback fs.readFile(path, cb) Legacy code, event-driven patterns
Promise fs.promises.readFile(path) Modern async/await code ✅
Synchronous fs.readFileSync(path) Scripts, startup config (blocks event loop!)

Common operations:

Operation Method
Read file readFile / readFileSync
Write file writeFile (overwrites) / appendFile (appends)
Delete file unlink
Rename/move rename
Check existence access / stat
Create directory mkdir (with { recursive: true })
Delete directory rm (with { recursive: true, force: true })
List directory readdir
Watch changes watch / watchFile
Copy file copyFile

File descriptors and flags:

Flag Meaning
'r' Read (error if file doesn't exist)
'w' Write (creates/truncates file)
'a' Append (creates file if needed)
'wx' Write exclusive (error if file exists)
'r+' Read and write

The path module — always use it: Never concatenate paths with string operations. Use path.join() and path.resolve() for cross-platform compatibility (Windows uses \\, Unix uses /).

🏠 Real-world analogy: The fs module is like a filing cabinet. You can read documents (readFile), add new ones (writeFile), throw some away (unlink), reorganize folders (rename), and even set a notification when someone changes a file (watch).

💻 Code Example

codeTap to expand ⛶
1// File System Operations — Comprehensive Guide
2
3const fs = require("fs");
4const fsp = require("fs").promises; // Promise-based API
5const path = require("path");
6
7// 1. Reading files
8async function readFileExamples() {
9 // Promise-based (recommended)
10 const content = await fsp.readFile("data.txt", "utf-8");
11 console.log("Content:", content);
12
13 // Read as Buffer (binary files)
14 const buffer = await fsp.readFile("image.png");
15 console.log("File size:", buffer.length, "bytes");
16
17 // Read JSON file
18 const jsonStr = await fsp.readFile("config.json", "utf-8");
19 const config = JSON.parse(jsonStr);
20
21 // Synchronous (only for startup/scripts)
22 const syncContent = fs.readFileSync("data.txt", "utf-8");
23}
24
25// 2. Writing files
26async function writeFileExamples() {
27 // Write (creates or overwrites)
28 await fsp.writeFile("output.txt", "Hello, World!\n", "utf-8");
29
30 // Write JSON
31 const data = { name: "Alice", age: 30 };
32 await fsp.writeFile("data.json", JSON.stringify(data, null, 2));
33
34 // Append to file
35 await fsp.appendFile("logs.txt", `[${new Date().toISOString()}] Event occurred\n`);
36
37 // Write with exclusive flag (error if exists)
38 try {
39 await fsp.writeFile("unique.txt", "data", { flag: "wx" });
40 } catch (err) {
41 if (err.code === "EEXIST") console.log("File already exists");
42 }
43}
44
45// 3. Directory operations
46async function directoryExamples() {
47 // Create directory (recursive = create parent dirs too)
48 await fsp.mkdir("output/reports/2024", { recursive: true });
49
50 // List directory contents
51 const files = await fsp.readdir("src");
52 console.log("Files:", files);
53
54 // List with file type info
55 const entries = await fsp.readdir("src", { withFileTypes: true });
56 entries.forEach((entry) => {
57 console.log(`${entry.isDirectory() ? "📁" : "📄"} ${entry.name}`);
58 });
59
60 // Remove directory (recursive)
61 await fsp.rm("temp", { recursive: true, force: true });
62}
63
64// 4. File metadata
65async function statExamples() {
66 const stats = await fsp.stat("data.txt");
67 console.log("Is file:", stats.isFile());
68 console.log("Is directory:", stats.isDirectory());
69 console.log("Size:", stats.size, "bytes");
70 console.log("Created:", stats.birthtime);
71 console.log("Modified:", stats.mtime);
72 console.log("Permissions:", stats.mode.toString(8));
73}
74
75// 5. Path module — cross-platform path handling
76function pathExamples() {
77 // ✅ GOOD: Use path.join for cross-platform paths
78 const filePath = path.join(__dirname, "data", "users.json");
79 // Linux: /app/data/users.json
80 // Windows: C:\app\data\users.json
81
82 // path.resolve — Always returns absolute path
83 const absolute = path.resolve("data", "users.json");
84 console.log("Absolute:", absolute);
85
86 // Parse a path into components
87 const parsed = path.parse("/home/user/docs/report.pdf");
88 console.log(parsed);
89 // { root: '/', dir: '/home/user/docs', base: 'report.pdf',
90 // ext: '.pdf', name: 'report' }
91
92 console.log("Extension:", path.extname("app.test.js")); // .js
93 console.log("Basename:", path.basename("/foo/bar.txt")); // bar.txt
94 console.log("Directory:", path.dirname("/foo/bar.txt")); // /foo
95 console.log("Relative:", path.relative("/a/b", "/a/c")); // ../c
96}
97
98// 6. Safe file existence check
99async function fileExists(filePath) {
100 try {
101 await fsp.access(filePath, fs.constants.F_OK);
102 return true;
103 } catch {
104 return false;
105 }
106}
107
108// 7. Recursive directory walker
109async function walkDir(dir) {
110 const entries = await fsp.readdir(dir, { withFileTypes: true });
111 const files = [];
112
113 for (const entry of entries) {
114 const fullPath = path.join(dir, entry.name);
115 if (entry.isDirectory()) {
116 files.push(...(await walkDir(fullPath)));
117 } else {
118 files.push(fullPath);
119 }
120 }
121 return files;
122}
123
124// Usage
125// const allFiles = await walkDir("./src");
126// console.log("All files:", allFiles);
127
128// 8. File watching
129function watchExample() {
130 const watcher = fs.watch("./src", { recursive: true }, (eventType, filename) => {
131 console.log(`[${eventType}] ${filename}`);
132 });
133
134 // Clean up on exit
135 process.on("SIGINT", () => {
136 watcher.close();
137 process.exit(0);
138 });
139}
140
141// 9. Temp files and atomic writes
142async function atomicWrite(filePath, data) {
143 const tempPath = `${filePath}.${process.pid}.tmp`;
144 try {
145 await fsp.writeFile(tempPath, data);
146 await fsp.rename(tempPath, filePath); // Atomic on same filesystem
147 } catch (err) {
148 // Clean up temp file on failure
149 await fsp.unlink(tempPath).catch(() => {});
150 throw err;
151 }
152}
153
154pathExamples();

🏋️ Practice Exercise

Exercises:

  1. Write a script that reads a JSON file, modifies a field, and writes it back atomically
  2. Build a recursive directory listing tool that displays a tree structure with sizes
  3. Create a log rotator: when app.log exceeds 10MB, rename it to app.log.1 and start a new one
  4. Implement a file copy function that works for both text and binary files
  5. Build a file watcher that debounces changes and logs which files were modified
  6. Write a find command clone that searches for files matching a glob pattern recursively

⚠️ Common Mistakes

  • Using fs.existsSync() before reading — it's a TOCTOU race condition; instead, try the operation and handle ENOENT in the catch block

  • Concatenating paths with + or template literals — __dirname + '/data' breaks on Windows; use path.join(__dirname, 'data') instead

  • Using synchronous fs methods in a server — readFileSync blocks the event loop and freezes ALL concurrent connections

  • Not handling EACCES (permission denied) and ENOENT (not found) errors separately — different errors need different responses

  • Forgetting { recursive: true } in mkdir — without it, creating nested directories fails if parents don't exist

💼 Interview Questions

🎤 Mock Interview

Mock interview is powered by AI for File System Operations (fs Module). Login to unlock this feature.