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
1// File System Operations — Comprehensive Guide23const fs = require("fs");4const fsp = require("fs").promises; // Promise-based API5const path = require("path");67// 1. Reading files8async function readFileExamples() {9 // Promise-based (recommended)10 const content = await fsp.readFile("data.txt", "utf-8");11 console.log("Content:", content);1213 // Read as Buffer (binary files)14 const buffer = await fsp.readFile("image.png");15 console.log("File size:", buffer.length, "bytes");1617 // Read JSON file18 const jsonStr = await fsp.readFile("config.json", "utf-8");19 const config = JSON.parse(jsonStr);2021 // Synchronous (only for startup/scripts)22 const syncContent = fs.readFileSync("data.txt", "utf-8");23}2425// 2. Writing files26async function writeFileExamples() {27 // Write (creates or overwrites)28 await fsp.writeFile("output.txt", "Hello, World!\n", "utf-8");2930 // Write JSON31 const data = { name: "Alice", age: 30 };32 await fsp.writeFile("data.json", JSON.stringify(data, null, 2));3334 // Append to file35 await fsp.appendFile("logs.txt", `[${new Date().toISOString()}] Event occurred\n`);3637 // 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}4445// 3. Directory operations46async function directoryExamples() {47 // Create directory (recursive = create parent dirs too)48 await fsp.mkdir("output/reports/2024", { recursive: true });4950 // List directory contents51 const files = await fsp.readdir("src");52 console.log("Files:", files);5354 // List with file type info55 const entries = await fsp.readdir("src", { withFileTypes: true });56 entries.forEach((entry) => {57 console.log(`${entry.isDirectory() ? "📁" : "📄"} ${entry.name}`);58 });5960 // Remove directory (recursive)61 await fsp.rm("temp", { recursive: true, force: true });62}6364// 4. File metadata65async 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}7475// 5. Path module — cross-platform path handling76function pathExamples() {77 // ✅ GOOD: Use path.join for cross-platform paths78 const filePath = path.join(__dirname, "data", "users.json");79 // Linux: /app/data/users.json80 // Windows: C:\app\data\users.json8182 // path.resolve — Always returns absolute path83 const absolute = path.resolve("data", "users.json");84 console.log("Absolute:", absolute);8586 // Parse a path into components87 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' }9192 console.log("Extension:", path.extname("app.test.js")); // .js93 console.log("Basename:", path.basename("/foo/bar.txt")); // bar.txt94 console.log("Directory:", path.dirname("/foo/bar.txt")); // /foo95 console.log("Relative:", path.relative("/a/b", "/a/c")); // ../c96}9798// 6. Safe file existence check99async function fileExists(filePath) {100 try {101 await fsp.access(filePath, fs.constants.F_OK);102 return true;103 } catch {104 return false;105 }106}107108// 7. Recursive directory walker109async function walkDir(dir) {110 const entries = await fsp.readdir(dir, { withFileTypes: true });111 const files = [];112113 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}123124// Usage125// const allFiles = await walkDir("./src");126// console.log("All files:", allFiles);127128// 8. File watching129function watchExample() {130 const watcher = fs.watch("./src", { recursive: true }, (eventType, filename) => {131 console.log(`[${eventType}] ${filename}`);132 });133134 // Clean up on exit135 process.on("SIGINT", () => {136 watcher.close();137 process.exit(0);138 });139}140141// 9. Temp files and atomic writes142async 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 filesystem147 } catch (err) {148 // Clean up temp file on failure149 await fsp.unlink(tempPath).catch(() => {});150 throw err;151 }152}153154pathExamples();
🏋️ Practice Exercise
Exercises:
- Write a script that reads a JSON file, modifies a field, and writes it back atomically
- Build a recursive directory listing tool that displays a tree structure with sizes
- Create a log rotator: when
app.logexceeds 10MB, rename it toapp.log.1and start a new one - Implement a file copy function that works for both text and binary files
- Build a file watcher that debounces changes and logs which files were modified
- Write a
findcommand 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 blockConcatenating paths with
+or template literals —__dirname + '/data'breaks on Windows; usepath.join(__dirname, 'data')insteadUsing synchronous fs methods in a server —
readFileSyncblocks the event loop and freezes ALL concurrent connectionsNot handling EACCES (permission denied) and ENOENT (not found) errors separately — different errors need different responses
Forgetting
{ recursive: true }inmkdir— 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.