Complete Node.js File System Tutorial

Read, write, stream, and manage files in real-world Node.js applications

Hands-on Production Ready Best Practices

Table of Contents

  1. Introduction & Setup
  2. Reading Files
  3. Writing Files
  4. Working with Directories
  5. File Information & Permissions
  6. Working with File Paths
  7. Streaming Large Files
  8. Real-World Examples
  9. Error Handling Best Practices
  10. Performance Tips

1. Introduction & Setup

What is the FS Module?

The Node.js fs module lets you interact with files and directories: read, write, update, delete, inspect metadata, and manage folder structures.

Installation & Setup

// Built into Node.js - no install needed
const fsPromises = require('node:fs/promises'); // Promise-based (recommended)
const fs = require('node:fs');                  // Callback/sync APIs
mkdir node-fs-tutorial
cd node-fs-tutorial
npm init -y
echo "Hello World" > test.txt

2. Reading Files

Basic Reading

const fs = require('node:fs/promises');
async function readFileExample() {
  try {
    const content = await fs.readFile('test.txt', 'utf8');
    console.log(content);
    const buffer = await fs.readFile('image.jpg');
    console.log(buffer.length);
  } catch (error) {
    console.error(error.message);
  }
}

Read JSON

const jsonContent = await fs.readFile('data.json', 'utf8');
const parsed = JSON.parse(jsonContent);
console.log(parsed.name);

Line-by-line Reading

const readline = require('node:readline');
const fsCb = require('node:fs');
const rl = readline.createInterface({ input: fsCb.createReadStream('large-file.txt'), crlfDelay: Infinity });
for await (const line of rl) { console.log(line); }

3. Writing Files

Basic Writing

await fs.writeFile('output.txt', 'Hello, Node.js!');
await fs.appendFile('output.txt', '\nAppended line');
await fs.writeFile('binary.bin', Buffer.from([0x48, 0x65, 0x6c, 0x6c, 0x6f]));

Write JSON

const data = { users: [{ id: 1, name: 'Alice' }] };
await fs.writeFile('users.json', JSON.stringify(data, null, 2));
await fs.writeFile('users.min.json', JSON.stringify(data));

Conditional Writing

try { await fs.access('config.json'); }
catch { await fs.writeFile('config.json', JSON.stringify({ theme: 'dark' })); }

4. Working with Directories

await fs.mkdir('my-folder');
await fs.mkdir('parent/child/grandchild', { recursive: true });
const contents = await fs.readdir('parent');
const detailed = await fs.readdir('parent', { withFileTypes: true });

Recursive Traversal

async function listAllFiles(dir) {
  const items = await fs.readdir(dir, { withFileTypes: true });
  for (const item of items) {
    const full = require('node:path').join(dir, item.name);
    if (item.isDirectory()) await listAllFiles(full);
    else console.log(full);
  }
}

Copy Directory

async function copyDirectory(src, dst) {
  await fs.mkdir(dst, { recursive: true });
  const items = await fs.readdir(src, { withFileTypes: true });
  for (const item of items) {
    const path = require('node:path');
    const s = path.join(src, item.name);
    const d = path.join(dst, item.name);
    if (item.isDirectory()) await copyDirectory(s, d);
    else await fs.copyFile(s, d);
  }
}

5. File Information & Permissions

const stats = await fs.stat('test.txt');
console.log(stats.size, stats.mtime, stats.isFile());
console.log(stats.mode.toString(8));

Permissions

await fs.writeFile('script.js', 'console.log("Hello");');
await fs.chmod('script.js', 0o755); // executable
await fs.chmod('script.js', 0o444); // read-only
await fs.chmod('script.js', 0o644); // restore

6. Working with File Paths

const path = require('node:path');
const filePath = '/users/john/projects/app/config.json';
console.log(path.dirname(filePath));
console.log(path.basename(filePath));
console.log(path.extname(filePath));
console.log(path.join(__dirname, 'config', 'app.json'));
console.log(path.resolve('relative/path/file.txt'));

Cross-platform Tip

// Avoid manual separators
const good = path.join(__dirname, 'data', 'file.txt');

7. Streaming Large Files

const fsCb = require('node:fs');
const readStream = fsCb.createReadStream('large-file.log', { highWaterMark: 64 * 1024 });
const writeStream = fsCb.createWriteStream('output.log');
readStream.pipe(writeStream);

Compress While Streaming

const zlib = require('node:zlib');
fsCb.createReadStream('large-file.log')
  .pipe(zlib.createGzip())
  .pipe(fsCb.createWriteStream('large-file.log.gz'));

8. Real-World Examples

File Backup System

class BackupSystem {
  async backup() { // create timestamped backup + manifest }
  async copyDirectory(src, dest) { // recursive copy }
}

Config Manager

class ConfigManager {
  async load() { // load or create default config }
  async save() { // write + backup }
  get(key) { // dot path access }
}

Log Rotator

class LogRotator {
  async rotate(logFile) { // rotate by size threshold }
  async rotateFiles(baseName) { // keep max backup files }
}

9. Error Handling Best Practices

class FileSystemError extends Error {
  constructor(message, code, operation, path) {
    super(message);
    this.code = code; this.operation = operation; this.path = path;
  }
}

Retry with Backoff

async function withRetry(operation, retries = 3, delay = 1000) {
  for (let i = 0; i < retries; i++) {
    try { return await operation(); }
    catch (e) { if (i === retries - 1) throw e; await new Promise(r => setTimeout(r, delay)); delay *= 2; }
  }
}

10. Performance Tips

Batch Operations

// Parallel reads
const results = await Promise.all(files.map(f => fs.readFile(f, 'utf8')));

Simple File Cache

class FileCache {
  constructor(maxSize = 100) { this.cache = new Map(); this.maxSize = maxSize; }
  async readFile(filePath) { // cache hit/miss logic }
}

Memory-efficient line processing

// process stream chunks without loading full file
await processLargeFileEfficiently('huge-file.log', line => {
  if (line.includes('ERROR')) console.log(line);
});

Quick Reference Cheatsheet

await fs.readFile('file.txt', 'utf8');
await fs.writeFile('file.txt', 'content');
await fs.appendFile('file.txt', 'more\n');
await fs.unlink('file.txt');
await fs.mkdir('folder', { recursive: true });
await fs.readdir('folder');
await fs.copyFile('a.txt', 'b.txt');
await fs.rename('old.txt', 'new.txt');

10 Interview Questions + 10 MCQs

Interview Pattern 10 Q&A
1Why prefer fs/promises API?easy
Answer: Cleaner async/await code, better readability, and easier error handling.
2When should you use streams instead of readFile?easy
Answer: For large files to reduce memory usage and improve throughput.
3Difference between writeFile and appendFile?easy
Answer: writeFile overwrites file; appendFile adds content at end.
4Why use path.join over manual string concatenation?medium
Answer: path.join is cross-platform safe and avoids separator issues.
5How to check if file exists without race conditions?medium
Answer: Prefer trying the operation and handling ENOENT, or use access carefully.
6What does recursive option in mkdir do?easy
Answer: Creates nested directories if missing.
7How do permissions like 755/644 differ?medium
Answer: 755 allows execute for owner/group/others; 644 is read/write owner and read-only others.
8Why implement retry logic in FS operations?medium
Answer: To handle transient errors (locks, temporary IO issues) with resilience.
9How to process huge logs efficiently?hard
Answer: Use streaming/chunk processing and line-wise handling instead of loading full files.
10What is a safe backup strategy before overwrite?hard
Answer: Copy existing file/folder to timestamped backup and verify write success.

10 Node.js File System MCQs

1

Which import is promise-based fs API?

Arequire('fs')
Brequire('node:fs/promises')
Crequire('fs-async')
Drequire('promise-fs')
Explanation: Promise API is available in node:fs/promises.
2

Which method appends to file?

AwriteFile
BappendFile
CaddFile
DconcatFile
Explanation: appendFile adds content at end.
3

Best for huge file processing?

AreadFileSync
BcreateReadStream
CJSON.parse directly
DBuffer.concat all
Explanation: Streams are memory efficient.
4

Option to create nested directories?

Adeep: true
Brecursive: true
Cparents: true
Dtree: true
Explanation: Use { recursive: true } in mkdir.
5

Which module handles cross-platform paths?

Aurl
Bos
Cpath
Dstream
Explanation: Use Node path module.
6

Which call gets file metadata?

Afs.meta
Bfs.stat
Cfs.info
Dfs.fileInfo
Explanation: fs.stat returns file stats object.
7

Permission mode 0o444 means:

ARead-only
BExecutable
CWrite-only
DNo permissions
Explanation: 444 is read-only for owner/group/others.
8

Which method removes a file?

Afs.delete
Bfs.unlink
Cfs.removeFile
Dfs.rmFile
Explanation: Use unlink for files.
9

Best strategy for transient IO failures?

AImmediate exit
BRetry with backoff
CIgnore error
DInfinite loop no delay
Explanation: Backoff retries improve resilience.
10

Which is best for checking detailed directory entries?

Areaddir(path)
Breaddir(path, { withFileTypes: true })
Cstat(path) only
Dfs.list(path)
Explanation: withFileTypes returns Dirent objects for files/directories.