fs
The fs module provides file system operations for reading, writing, and manipulating files and directories. It supports three API styles: synchronous, asynchronous callback-based, and promise-based.
Import
const fs = require('fs');
// For callback-based async API
const fs = require('fs').promises;
// For promise-based API
const { readFileSync, writeFileSync } = require('fs');
// For synchronous API
API Styles
1. Synchronous Methods
Blocking operations that return results directly. Method names end with Sync.
2. Asynchronous Callback-based Methods
Non-blocking operations that use callbacks. These are the standard methods without Sync suffix.
3. Promise-based API (fs.promises)
Non-blocking operations that return Promises. Available via require('fs').promises or require('fs/promises').
API Reference
Reading Files
fs.readFile(path[, options], callback)
fs.readFileSync(path[, options])
fsPromises.readFile(path[, options])
Reads the entire contents of a file.
Options:
encoding- Character encoding (default: null, returns Buffer)flag- File system flag (default: 'r')
Writing Files
fs.writeFile(file, data[, options], callback)
fs.writeFileSync(file, data[, options])
fsPromises.writeFile(file, data[, options])
Writes data to a file, replacing the file if it already exists.
Appending to Files
fs.appendFile(path, data[, options], callback)
fs.appendFileSync(path, data[, options])
fsPromises.appendFile(path, data[, options])
Appends data to a file, creating the file if it does not exist.
File Information
fs.stat(path[, options], callback)
fs.statSync(path[, options])
fsPromises.stat(path[, options])
Returns file/directory statistics.
File Operations
fs.unlink(path, callback)
fs.unlinkSync(path)
fsPromises.unlink(path)
Deletes a file.
fs.rename(oldPath, newPath, callback)
fs.renameSync(oldPath, newPath)
fsPromises.rename(oldPath, newPath)
Renames or moves a file.
fs.copyFile(src, dest[, mode], callback)
fs.copyFileSync(src, dest[, mode])
fsPromises.copyFile(src, dest[, mode])
Copies a file.
Directory Operations
fs.mkdir(path[, options], callback)
fs.mkdirSync(path[, options])
fsPromises.mkdir(path[, options])
Creates a directory.
Options:
recursive- Create parent directories if needed (default: false)mode- Directory permissions (default: 0o777)
fs.readdir(path[, options], callback)
fs.readdirSync(path[, options])
fsPromises.readdir(path[, options])
Reads the contents of a directory.
fs.rmdir(path[, options], callback)
fs.rmdirSync(path[, options])
fsPromises.rmdir(path[, options])
Removes a directory.
fs.rm(path[, options], callback)
fs.rmSync(path[, options])
fsPromises.rm(path[, options])
Removes files and directories (more flexible than rmdir).
File Existence
fs.access(path[, mode], callback)
fs.accessSync(path[, mode])
fsPromises.access(path[, mode])
Tests file/directory accessibility and permissions.
fs.exists(path, callback) [Deprecated]
Use fs.access() instead.
File Watching
fs.watch(filename[, options][, listener])
Watches for changes on a file or directory.
fs.watchFile(filename[, options], listener)
Watches for changes on a file.
Stream APIs
fs.createReadStream(path[, options])
Creates a readable stream.
fs.createWriteStream(path[, options])
Creates a writable stream.
Examples
Reading Files (All Three Styles)
const fs = require('fs');
const fsPromises = require('fs').promises;
export async function handler(event) {
const filePath = '/tmp/test.txt';
// Create test file
fs.writeFileSync(filePath, 'Hello, World!');
// 1. Synchronous
const contentSync = fs.readFileSync(filePath, 'utf8');
// 2. Callback-based
const contentCallback = await new Promise((resolve, reject) => {
fs.readFile(filePath, 'utf8', (err, data) => {
if (err) reject(err);
else resolve(data);
});
});
// 3. Promise-based
const contentPromise = await fsPromises.readFile(filePath, 'utf8');
return {
sync: contentSync,
callback: contentCallback,
promise: contentPromise,
allMatch: contentSync === contentCallback && contentCallback === contentPromise
};
}
Writing Files (All Three Styles)
const fs = require('fs');
const fsPromises = require('fs').promises;
export async function handler(event) {
const content = event.content || 'File content';
// 1. Synchronous
fs.writeFileSync('/tmp/sync.txt', content, 'utf8');
// 2. Callback-based
await new Promise((resolve, reject) => {
fs.writeFile('/tmp/callback.txt', content, 'utf8', (err) => {
if (err) reject(err);
else resolve();
});
});
// 3. Promise-based
await fsPromises.writeFile('/tmp/promise.txt', content, 'utf8');
// Verify
const sync = fs.readFileSync('/tmp/sync.txt', 'utf8');
const callback = fs.readFileSync('/tmp/callback.txt', 'utf8');
const promise = fs.readFileSync('/tmp/promise.txt', 'utf8');
return {
allWritten: sync === content && callback === content && promise === content
};
}
Working with JSON Files
const fs = require('fs').promises;
export async function handler(event) {
const filePath = '/tmp/data.json';
// Write JSON
const data = {
users: [
{ id: 1, name: 'Alice', email: 'alice@example.com' },
{ id: 2, name: 'Bob', email: 'bob@example.com' }
],
timestamp: new Date().toISOString()
};
await fs.writeFile(filePath, JSON.stringify(data, null, 2), 'utf8');
// Read JSON
const content = await fs.readFile(filePath, 'utf8');
const parsed = JSON.parse(content);
return {
written: data,
read: parsed,
match: JSON.stringify(data) === JSON.stringify(parsed)
};
}
Directory Operations
const fs = require('fs').promises;
const path = require('path');
export async function handler(event) {
const baseDir = '/tmp/myapp';
const subDir = path.join(baseDir, 'data', 'users');
// Create nested directories
await fs.mkdir(subDir, { recursive: true });
// Create some files
await fs.writeFile(path.join(subDir, 'user1.json'), '{"name":"Alice"}');
await fs.writeFile(path.join(subDir, 'user2.json'), '{"name":"Bob"}');
await fs.writeFile(path.join(baseDir, 'config.json'), '{"version":"1.0"}');
// List directory contents
const files = await fs.readdir(subDir);
const allFiles = await fs.readdir(baseDir, { recursive: true });
// Get file stats
const stats = await fs.stat(subDir);
return {
created: subDir,
files,
allFiles,
isDirectory: stats.isDirectory()
};
}
File Statistics and Metadata
const fs = require('fs').promises;
export async function handler(event) {
const filePath = '/tmp/document.txt';
// Create file
await fs.writeFile(filePath, 'Sample content');
// Get stats
const stats = await fs.stat(filePath);
return {
size: stats.size,
isFile: stats.isFile(),
isDirectory: stats.isDirectory(),
created: stats.birthtime,
modified: stats.mtime,
accessed: stats.atime,
mode: stats.mode.toString(8),
permissions: stats.mode & 0o777
};
}
Copying and Moving Files
const fs = require('fs').promises;
export async function handler(event) {
const source = '/tmp/source.txt';
const copyDest = '/tmp/copy.txt';
const moveDest = '/tmp/moved.txt';
// Create source file
await fs.writeFile(source, 'Original content');
// Copy file
await fs.copyFile(source, copyDest);
// Move file (rename)
await fs.rename(source, moveDest);
// Verify
const copyContent = await fs.readFile(copyDest, 'utf8');
const moveContent = await fs.readFile(moveDest, 'utf8');
// Check if source still exists
let sourceExists = false;
try {
await fs.access(source);
sourceExists = true;
} catch (err) {
sourceExists = false;
}
return {
copyContent,
moveContent,
sourceExists,
contentsMatch: copyContent === moveContent
};
}
Appending to Files
const fs = require('fs').promises;
export async function handler(event) {
const logFile = '/tmp/app.log';
// Initial write
await fs.writeFile(logFile, 'Log started\n');
// Append entries
await fs.appendFile(logFile, `[${new Date().toISOString()}] User logged in\n`);
await fs.appendFile(logFile, `[${new Date().toISOString()}] Action performed\n`);
await fs.appendFile(logFile, `[${new Date().toISOString()}] User logged out\n`);
// Read full log
const log = await fs.readFile(logFile, 'utf8');
return {
log,
lines: log.split('\n').filter(line => line.length > 0).length
};
}
Checking File Existence
const fs = require('fs').promises;
export async function handler(event) {
const existingFile = '/tmp/exists.txt';
const missingFile = '/tmp/missing.txt';
// Create one file
await fs.writeFile(existingFile, 'Content');
// Check existence using access
async function fileExists(path) {
try {
await fs.access(path);
return true;
} catch {
return false;
}
}
const exists1 = await fileExists(existingFile);
const exists2 = await fileExists(missingFile);
return {
existingFile: exists1,
missingFile: exists2
};
}
Reading Directories Recursively
const fs = require('fs').promises;
const path = require('path');
export async function handler(event) {
const rootDir = '/tmp/project';
// Create directory structure
await fs.mkdir(path.join(rootDir, 'src'), { recursive: true });
await fs.mkdir(path.join(rootDir, 'tests'), { recursive: true });
await fs.writeFile(path.join(rootDir, 'README.md'), '# Project');
await fs.writeFile(path.join(rootDir, 'src', 'index.js'), 'console.log("hi")');
await fs.writeFile(path.join(rootDir, 'tests', 'test.js'), 'test()');
// Read recursively
async function getAllFiles(dir) {
const entries = await fs.readdir(dir, { withFileTypes: true });
const files = [];
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
const subFiles = await getAllFiles(fullPath);
files.push(...subFiles);
} else {
files.push(fullPath);
}
}
return files;
}
const allFiles = await getAllFiles(rootDir);
return {
rootDir,
files: allFiles.map(f => path.relative(rootDir, f))
};
}
Stream Reading for Large Files
const fs = require('fs');
export async function handler(event) {
const filePath = '/tmp/large-file.txt';
// Create large file
const lines = Array.from({ length: 10000 }, (_, i) => `Line ${i + 1}`);
fs.writeFileSync(filePath, lines.join('\n'));
// Stream read
return new Promise((resolve, reject) => {
const stream = fs.createReadStream(filePath, { encoding: 'utf8' });
let lineCount = 0;
let buffer = '';
stream.on('data', (chunk) => {
buffer += chunk;
const lines = buffer.split('\n');
buffer = lines.pop(); // Keep incomplete line
lineCount += lines.length;
});
stream.on('end', () => {
if (buffer) lineCount++; // Count last line
resolve({
filePath,
lineCount,
fileSize: fs.statSync(filePath).size
});
});
stream.on('error', reject);
});
}
Stream Writing
const fs = require('fs');
export async function handler(event) {
const filePath = '/tmp/output.txt';
return new Promise((resolve, reject) => {
const stream = fs.createWriteStream(filePath);
// Write data in chunks
for (let i = 0; i < 1000; i++) {
stream.write(`Line ${i + 1}\n`);
}
stream.end();
stream.on('finish', () => {
const stats = fs.statSync(filePath);
resolve({
filePath,
bytesWritten: stats.size,
lines: 1000
});
});
stream.on('error', reject);
});
}
Deleting Files and Directories
const fs = require('fs').promises;
const path = require('path');
export async function handler(event) {
const testDir = '/tmp/to-delete';
// Create structure
await fs.mkdir(path.join(testDir, 'subdir'), { recursive: true });
await fs.writeFile(path.join(testDir, 'file1.txt'), 'content');
await fs.writeFile(path.join(testDir, 'subdir', 'file2.txt'), 'content');
// Delete individual file
await fs.unlink(path.join(testDir, 'file1.txt'));
// Delete directory recursively
await fs.rm(testDir, { recursive: true, force: true });
// Verify deletion
let exists = false;
try {
await fs.access(testDir);
exists = true;
} catch {
exists = false;
}
return {
deleted: !exists,
message: 'Directory and contents deleted'
};
}
Binary File Operations
const fs = require('fs').promises;
export async function handler(event) {
const filePath = '/tmp/binary-data.bin';
// Create binary data
const buffer = Buffer.alloc(256);
for (let i = 0; i < 256; i++) {
buffer[i] = i;
}
// Write binary file
await fs.writeFile(filePath, buffer);
// Read binary file
const readBuffer = await fs.readFile(filePath);
// Compare
const match = buffer.equals(readBuffer);
return {
written: buffer.length,
read: readBuffer.length,
match,
first10Bytes: Array.from(readBuffer.slice(0, 10))
};
}
Temporary File Operations
const fs = require('fs').promises;
const path = require('path');
const crypto = require('crypto');
export async function handler(event) {
// Generate unique temp filename
const tempFileName = `temp-${crypto.randomBytes(8).toString('hex')}.txt`;
const tempPath = path.join('/tmp', tempFileName);
try {
// Create and use temp file
await fs.writeFile(tempPath, 'Temporary data');
const content = await fs.readFile(tempPath, 'utf8');
return {
tempFile: tempFileName,
content
};
} finally {
// Clean up temp file
try {
await fs.unlink(tempPath);
} catch {
// Ignore if file doesn't exist
}
}
}
Best Practices
- Prefer promise-based API - Use
fs.promisesfor cleaner async code - Avoid synchronous methods in production - They block the event loop
- Use streams for large files - More memory efficient than reading entire file
- Always handle errors - File operations can fail for many reasons
- Use path.join() - For cross-platform path construction
- Close file handles - Especially when using file descriptors
- Check file existence with access() - Not the deprecated
exists() - Use appropriate file flags - 'wx' to avoid overwriting existing files
File System Flags
'r'- Read (default)'w'- Write (truncates existing file)'a'- Append'x'- Exclusive (fails if file exists)'r+'- Read and write'w+'- Read and write (truncates)'a+'- Read and append'wx'- Write exclusive