How to Walk Directory Recursively in Node.js: Simple Guide
To walk a directory recursively in Node.js, use the
fs.promises API with async functions to read directory contents and check if each item is a file or folder. Recursively call the function on folders to explore all nested files and directories.Syntax
Use fs.promises.readdir(path, { withFileTypes: true }) to read directory entries with their types. Use Dirent.isDirectory() to check if an entry is a folder. Recursively call your async function on directories to walk deeper.
This pattern uses async/await for clean, readable code.
javascript
import { promises as fs } from 'fs'; async function walkDir(dir) { const entries = await fs.readdir(dir, { withFileTypes: true }); for (const entry of entries) { const fullPath = `${dir}/${entry.name}`; if (entry.isDirectory()) { await walkDir(fullPath); // recurse into subdirectory } else { console.log(fullPath); // handle file } } }
Example
This example shows how to print all files inside a directory and its subdirectories recursively using modern Node.js APIs.
javascript
import { promises as fs } from 'fs'; async function walkDir(dir) { const entries = await fs.readdir(dir, { withFileTypes: true }); for (const entry of entries) { const fullPath = `${dir}/${entry.name}`; if (entry.isDirectory()) { await walkDir(fullPath); } else { console.log(fullPath); } } } walkDir('./test-folder').catch(console.error);
Output
./test-folder/file1.txt
./test-folder/subfolder/file2.txt
./test-folder/subfolder/file3.txt
Common Pitfalls
- Not using
withFileTypes: truecauses you to get only file names, making it harder to check if an entry is a directory. - Forgetting to await recursive calls can cause unpredictable order or missed files.
- Using synchronous APIs blocks the event loop and slows down your app.
- Not handling errors can crash your program if a directory is unreadable.
javascript
import { promises as fs } from 'fs'; // Wrong: missing await on recursive call async function walkDirWrong(dir) { const entries = await fs.readdir(dir, { withFileTypes: true }); for (const entry of entries) { const fullPath = `${dir}/${entry.name}`; if (entry.isDirectory()) { walkDirWrong(fullPath); // missing await } else { console.log(fullPath); } } } // Right: await recursive call async function walkDirRight(dir) { const entries = await fs.readdir(dir, { withFileTypes: true }); for (const entry of entries) { const fullPath = `${dir}/${entry.name}`; if (entry.isDirectory()) { await walkDirRight(fullPath); } else { console.log(fullPath); } } }
Quick Reference
- fs.promises.readdir(path, { withFileTypes: true }): Reads directory entries with type info.
- Dirent.isDirectory(): Checks if entry is a directory.
- async/await: Use to handle asynchronous file system calls cleanly.
- Recursive calls: Call your function on subdirectories to walk deeply.
Key Takeaways
Use fs.promises.readdir with { withFileTypes: true } to get directory entries and their types.
Check if an entry is a directory with Dirent.isDirectory() before recursing.
Always await recursive calls to ensure proper order and completion.
Avoid synchronous file system calls to keep your app responsive.
Handle errors to prevent crashes when directories are inaccessible.