Node.js Guide
Node.js fs Module — Complete Guide
Every file operation you'll ever need: reading, writing, appending, deleting, watching, and manipulating paths.
Quick answer: Import fs/promises for async operations (await fs.readFile) — that's the modern default. Use plain fs only for sync operations like fs.readFileSync in startup scripts.
The Three APIs
// Sync (blocks — use in scripts)
const fs = require('fs');
fs.readFileSync('file.txt', 'utf8');
// Callback (old style — avoid in new code)
fs.readFile('file.txt', 'utf8', (err, data) => { ... });
// Promises (modern — use this)
const fs = require('fs/promises');
await fs.readFile('file.txt', 'utf8');
// Or with ES Modules
import { readFile } from 'fs/promises';
Reading Files
import { readFile } from 'fs/promises';
// Read as text
const text = await readFile('./notes.txt', 'utf8');
// Read as Buffer (binary)
const buffer = await readFile('./image.png');
// Sync version
import { readFileSync } from 'fs';
const text = readFileSync('./notes.txt', 'utf8');
Writing Files
import { writeFile } from 'fs/promises';
// Overwrites the file if it exists
await writeFile('./output.txt', 'Hello, Node!');
// With explicit encoding
await writeFile('./output.txt', 'Hello, Node!', 'utf8');
// Append instead of overwrite
import { appendFile } from 'fs/promises';
await appendFile('./log.txt', 'New log entry\n');
💡 Warn: writeFile overwrites silently. To append, use appendFile.
File Info & Checks
import { stat, access, existsSync } from 'fs';
import { constants } from 'fs';
// Check if a file exists (sync)
if (existsSync('./config.json')) {
console.log('Config found');
}
// Get file info
const stats = await stat('./notes.txt');
console.log(stats.size); // size in bytes
console.log(stats.isFile()); // true
console.log(stats.isDirectory()); // false
console.log(stats.mtime); // last modified
// Check access permissions
await access('./notes.txt', constants.R_OK | constants.W_OK);
Directories
import { mkdir, readdir, rmdir, rm } from 'fs/promises';
// Create a directory (with parents)
await mkdir('./uploads/users', { recursive: true });
// List directory contents
const files = await readdir('./src');
console.log(files); // ['index.js', 'utils.js']
// Delete empty directory
await rmdir('./empty-folder');
// Delete with contents (recursive)
await rm('./old-folder', { recursive: true, force: true });
Rename, Copy, Delete
import { rename, copyFile, unlink } from 'fs/promises';
// Move or rename
await rename('./old.txt', './new.txt');
// Copy a file
await copyFile('./source.txt', './backup.txt');
// Delete a file
await unlink('./temp.txt');
Streaming Large Files
For files over ~50MB, streaming prevents memory issues:
import { createReadStream, createWriteStream } from 'fs';
import { pipeline } from 'stream/promises';
const src = createReadStream('./big-file.mp4');
const dst = createWriteStream('./copy.mp4');
await pipeline(src, dst);
Streaming reads the file in chunks instead of loading it all at once.
Working with Paths
Always use the path module for cross-platform paths:
import path from 'path';
// Join path segments correctly
const filePath = path.join('users', 'sarah', 'profile.json');
// Get absolute path
const absolute = path.resolve('./data.json');
// Extract parts
path.basename('/users/sarah/file.txt'); // 'file.txt'
path.dirname('/users/sarah/file.txt'); // '/users/sarah'
path.extname('image.png'); // '.png'
Never concatenate paths with + or / — Windows uses \, Unix uses /. The path module handles it.
📋 Quick Reference
fs.readFile(path, encoding) Read a file
fs.writeFile(path, data) Write (overwrites)
fs.appendFile(path, data) Append
fs.unlink(path) Delete a file
fs.rename(old, new) Rename or move
fs.copyFile(src, dest) Copy
fs.stat(path) File info
fs.access(path) Check permissions
fs.mkdir(path, { recursive }) Create directory
fs.readdir(path) List directory
fs.rmdir(path) Delete empty directory
fs.rm(path, { recursive }) Delete with contents
fs.existsSync(path) Exists check (sync only)
❓ Frequently Asked Questions
What is the fs module?
Node.js's built-in library for reading, writing, and manipulating files.
Sync or async?
Async in production. Sync only for startup scripts and CLIs.
fs vs fs.promises?
;fs.promises returns Promises, so you can use await. Cleaner and modern.
How to check if a file exists?
Use fs.existsSync(path) for sync, or await fs.access(path) in try/catch for async.