Working with Directories
Beyond reading and writing files, Node.js lets you create, read, and manipulate directories. Understanding directory operations is essential for building applications that organize files, manage uploads, or process folder structures.
Creating Directories
Create a Single Directory
const fs = require('fs');
// Synchronous
fs.mkdirSync('new-folder');
// With callback
fs.mkdir('another-folder', (err) => {
if (err) {
console.error('Error creating directory:', err.message);
return;
}
console.log('Directory created!');
});
// With promises
const fsPromises = require('fs/promises');
await fsPromises.mkdir('promised-folder');
Create Nested Directories
Use the recursive option:
// Create parent/child/grandchild structure
fs.mkdirSync('parent/child/grandchild', { recursive: true });
// Works with promises too
await fsPromises.mkdir('src/components/buttons', { recursive: true });
Reading Directory Contents
List Files and Folders
const fs = require('fs');
// Get array of names
const items = fs.readdirSync('my-folder');
console.log(items); // ['file1.txt', 'subfolder', 'file2.js']
// With file types
const itemsWithTypes = fs.readdirSync('my-folder', { withFileTypes: true });
itemsWithTypes.forEach(item => {
if (item.isDirectory()) {
console.log(`[DIR] ${item.name}`);
} else if (item.isFile()) {
console.log(`[FILE] ${item.name}`);
}
});
Checking If Path Is Directory or File
const fs = require('fs');
// Using stat
const stats = fs.statSync('some-path');
console.log('Is directory:', stats.isDirectory());
console.log('Is file:', stats.isFile());
console.log('Size:', stats.size, 'bytes');
console.log('Modified:', stats.mtime);
// Using lstat (doesn't follow symlinks)
const lstats = fs.lstatSync('some-path');
console.log('Is symlink:', lstats.isSymbolicLink());
Recursive Directory Walking
List all files in a directory tree:
const fs = require('fs');
const path = require('path');
function walkDir(dir, callback) {
const items = fs.readdirSync(dir, { withFileTypes: true });
for (const item of items) {
const fullPath = path.join(dir, item.name);
if (item.isDirectory()) {
walkDir(fullPath, callback); // Recurse
} else {
callback(fullPath); // Process file
}
}
}
// Find all JavaScript files
walkDir('./src', (filepath) => {
if (filepath.endsWith('.js')) {
console.log(filepath);
}
});
Removing Directories
const fs = require('fs');
// Remove empty directory
fs.rmdirSync('empty-folder');
// Remove directory and contents (Node.js 14+)
fs.rmSync('folder-with-files', { recursive: true });
// Async with promises
const fsPromises = require('fs/promises');
await fsPromises.rm('folder', { recursive: true, force: true });
Safe Removal with force Option
// Won't throw error if directory doesn't exist
fs.rmSync('maybe-exists', { recursive: true, force: true });
Renaming and Moving
const fs = require('fs');
// Rename directory
fs.renameSync('old-name', 'new-name');
// Move directory (rename across paths)
fs.renameSync('source/folder', 'destination/folder');
// Rename file
fs.renameSync('old.txt', 'new.txt');
// Async version
await fsPromises.rename('old', 'new');
Watching Directories
Monitor directories for changes:
const fs = require('fs');
// Watch a directory
const watcher = fs.watch('src', { recursive: true }, (event, filename) => {
console.log(`Event: ${event}, File: ${filename}`);
});
// Events: 'rename' (create/delete) or 'change' (modify)
// Stop watching
watcher.close();
Getting Directory Information
const fs = require('fs');
// Get directory stats
const stats = fs.statSync('my-folder');
console.log({
isDirectory: stats.isDirectory(),
created: stats.birthtime,
modified: stats.mtime,
accessed: stats.atime
});
// Get absolute path
const path = require('path');
const absolutePath = path.resolve('relative/path');
console.log(absolutePath);
Practical Example: Project Scaffolding
Working with Temporary Directories
const fs = require('fs');
const os = require('os');
const path = require('path');
// Get system temp directory
const tmpDir = os.tmpdir();
console.log('Temp dir:', tmpDir);
// Create unique temp directory
const uniqueTmpDir = fs.mkdtempSync(path.join(tmpDir, 'myapp-'));
console.log('Created:', uniqueTmpDir);
// Clean up when done
fs.rmSync(uniqueTmpDir, { recursive: true });
Common Patterns
Ensure Directory Exists
function ensureDir(dirPath) {
fs.mkdirSync(dirPath, { recursive: true });
}
// Use before writing files
ensureDir('logs');
fs.writeFileSync('logs/app.log', 'Log entry');
Clean Directory
function cleanDir(dirPath) {
if (fs.existsSync(dirPath)) {
fs.rmSync(dirPath, { recursive: true });
}
fs.mkdirSync(dirPath, { recursive: true });
}
// Use for build/dist directories
cleanDir('dist');
Copy Directory
function copyDir(src, dest) {
fs.mkdirSync(dest, { recursive: true });
for (const item of fs.readdirSync(src, { withFileTypes: true })) {
const srcPath = path.join(src, item.name);
const destPath = path.join(dest, item.name);
if (item.isDirectory()) {
copyDir(srcPath, destPath);
} else {
fs.copyFileSync(srcPath, destPath);
}
}
}
Key Takeaways
- Use
mkdirSyncwith{ recursive: true }to create nested directories - Use
readdirSyncwith{ withFileTypes: true }to distinguish files from directories - Use
statSyncto get file/directory information - Use
rmSyncwith{ recursive: true, force: true }to remove directories - Use
renameSyncto rename or move directories - Use
watchto monitor directory changes - Always use
path.join()to build paths safely
Summary
You've learned how to create, read, modify, and delete directories in Node.js. You know how to walk directory trees recursively, watch for changes, and implement common patterns like ensuring directories exist or copying folder structures. These skills are essential for file management, build tools, and any application that works with the file system.
In the next module, you'll learn how to create HTTP servers and handle web requests with Node.js.

