Directories & File Info

Difficulty: Beginner

Beyond reading and writing file contents, you often need to work with the file system at a higher level: creating directories, listing directory contents, checking file metadata, renaming and deleting files, and watching for changes. The fs module provides all of these capabilities, and like file I/O, they come in callback, synchronous, and promise-based variants.

Creating directories is done with fs.mkdir(path, options). The most useful option is 'recursive: true', which creates all intermediate directories in the path if they do not exist (similar to 'mkdir -p' in shell). Without recursive, creating 'a/b/c' fails if 'a/b' does not exist. To remove a directory, use fs.rmdir (empty directories only) or fs.rm with the 'recursive: true' option (removes directory and all contents, like 'rm -rf'). The rm method with recursive was stabilized in Node.js 14.

fs.readdir(path, options) reads the contents of a directory and returns an array of filenames (strings). With the 'withFileTypes: true' option, it returns an array of Dirent objects that include type information (isFile(), isDirectory(), isSymbolicLink()). This is much more efficient than calling stat on each entry separately. For recursive directory listing, Node.js 18.17+ supports the 'recursive: true' option, which returns all files and subdirectories in the entire tree.

fs.stat(path) returns detailed metadata about a file or directory as a Stats object. Key properties include size (in bytes), birthtime (creation time), mtime (last modification time), atime (last access time), and methods like isFile(), isDirectory(), and isSymbolicLink(). The lstat variant does not follow symbolic links, returning info about the link itself rather than the target. Stat is useful for checking if a file exists, determining its size before reading, or implementing conditional logic based on modification time.

fs.rename(oldPath, newPath) moves or renames a file or directory. fs.unlink(path) deletes a file (not a directory). fs.copyFile(src, dest) copies a file. For watching file system changes, fs.watch(path, callback) monitors a file or directory for modifications. The callback receives the event type ('rename' or 'change') and the filename. fs.watch is useful for development tools, live reload, and file synchronization, though it has some platform-specific quirks. For production use, consider the chokidar library which provides a more reliable cross-platform API.

Code examples

Creating and Reading Directories

const fs = require('fs/promises');
const path = require('path');

async function directoryOps() {
  // Create nested directories recursively
  await fs.mkdir('demo/src/utils', { recursive: true });
  console.log('Directories created');

  // Create some files
  await fs.writeFile('demo/src/index.js', 'console.log("hi")');
  await fs.writeFile('demo/src/utils/helpers.js', '// helpers');
  await fs.writeFile('demo/README.md', '# Demo');

  // List directory contents (simple)
  const files = await fs.readdir('demo');
  console.log('demo/:', files);

  // List with file type info
  const entries = await fs.readdir('demo', { withFileTypes: true });
  entries.forEach(entry => {
    const type = entry.isDirectory() ? 'DIR' : 'FILE';
    console.log(`  ${type}: ${entry.name}`);
  });

  // Recursive listing (Node 18.17+)
  const allFiles = await fs.readdir('demo', { recursive: true });
  console.log('\nAll files:', allFiles);

  // Clean up
  await fs.rm('demo', { recursive: true });
  console.log('Cleaned up');
}

directoryOps();

mkdir with recursive:true creates all intermediate directories. readdir with withFileTypes:true returns Dirent objects so you can distinguish files from directories without extra stat calls. The recursive option in readdir lists the entire tree.

File Metadata with stat

const fs = require('fs/promises');

async function fileInfo() {
  // Create a test file
  await fs.writeFile('test.txt', 'Hello, World!');

  const stats = await fs.stat('test.txt');

  console.log('Is file:', stats.isFile());
  console.log('Is directory:', stats.isDirectory());
  console.log('Size:', stats.size, 'bytes');
  console.log('Created:', stats.birthtime.toISOString());
  console.log('Modified:', stats.mtime.toISOString());
  console.log('Accessed:', stats.atime.toISOString());

  // Check if file exists (modern approach)
  try {
    await fs.access('test.txt');
    console.log('\nFile exists');
  } catch {
    console.log('\nFile does not exist');
  }

  // Clean up
  await fs.unlink('test.txt');
  console.log('File deleted');
}

fileInfo();

fs.stat provides detailed metadata including size, timestamps, and type information. Use fs.access to check if a file exists rather than the deprecated fs.exists. unlink deletes a single file.

Rename, Copy, and Watch Files

const fs = require('fs/promises');
const fsSync = require('fs');

async function fileOperations() {
  // Create a file
  await fs.writeFile('original.txt', 'Original content');

  // Rename (move) a file
  await fs.rename('original.txt', 'renamed.txt');
  console.log('File renamed');

  // Copy a file
  await fs.copyFile('renamed.txt', 'backup.txt');
  console.log('File copied');

  // Verify both exist
  const files = await fs.readdir('.');
  const relevant = files.filter(f => f.includes('renamed') || f.includes('backup'));
  console.log('Files:', relevant);

  // Watch for changes (demo with sync API for simplicity)
  // const watcher = fsSync.watch('renamed.txt', (eventType, filename) => {
  //   console.log(`Event: ${eventType} on ${filename}`);
  // });
  // To stop watching: watcher.close();

  console.log('\nfs.watch monitors files for changes in real-time');
  console.log('Events: "change" (content modified) or "rename" (file renamed/deleted)');

  // Clean up
  await fs.unlink('renamed.txt');
  await fs.unlink('backup.txt');
  console.log('Cleaned up');
}

fileOperations();

fs.rename moves or renames files and directories. fs.copyFile creates a duplicate. fs.watch observes file changes in real-time, useful for development tools. For production file watching, use the chokidar library for better cross-platform reliability.

Key points

Concepts covered

mkdir, readdir, stat, rename, unlink, watch