Lesson 4 of 20

The File System (fs)

Reading and Writing Files

The fs (file system) module lets you interact with the file system — reading files, writing data, creating directories, and more. It provides both synchronous and asynchronous methods.

Asynchronous methods are preferred because they don't block the event loop. Synchronous methods (ending in 'Sync') block execution until the operation completes, which can freeze your server.

Example
const fs = require('fs');

// --- Asynchronous (non-blocking) ---

// Reading a file
fs.readFile('message.txt', 'utf8', (err, data) => {
  if (err) {
    console.error('Error reading file:', err.message);
    return;
  }
  console.log('File contents:', data);
});

// Writing a file (creates or overwrites)
fs.writeFile('output.txt', 'Hello, Node.js!', (err) => {
  if (err) throw err;
  console.log('File written successfully');
});

// Appending to a file
fs.appendFile('log.txt', 'New log entry\n', (err) => {
  if (err) throw err;
  console.log('Data appended');
});

// --- Synchronous (blocking) ---
const data = fs.readFileSync('message.txt', 'utf8');
console.log(data);
  • fs.readFile() — Read file contents asynchronously
  • fs.writeFile() — Write data to a file (creates or overwrites)
  • fs.appendFile() — Append data to the end of a file
  • fs.readFileSync() — Synchronous version (blocks the event loop)
  • Always pass 'utf8' encoding to get a string instead of a Buffer
Notes
  • Never use synchronous fs methods in a server or API handler. They block the entire event loop, preventing other requests from being processed.

fs.promises and Async/Await

Modern Node.js provides fs.promises, which returns Promises instead of using callbacks. This lets you use async/await for cleaner, more readable file operations.

The fs.promises API is the recommended approach for new projects. It avoids callback hell and integrates naturally with try/catch error handling.

Example
const fs = require('fs').promises;
const path = require('path');

async function processFiles() {
  try {
    // Read a file
    const data = await fs.readFile('input.txt', 'utf8');
    console.log('Read:', data);

    // Write to a file
    await fs.writeFile('output.txt', data.toUpperCase());
    console.log('Written successfully');

    // Check if file exists
    try {
      await fs.access('config.json');
      console.log('Config file exists');
    } catch {
      console.log('Config file not found');
    }

    // Create a directory
    await fs.mkdir('logs', { recursive: true });

    // List files in a directory
    const files = await fs.readdir('.');
    console.log('Files:', files);

    // Get file info
    const stats = await fs.stat('output.txt');
    console.log('Size:', stats.size, 'bytes');
  } catch (err) {
    console.error('Error:', err.message);
  }
}

processFiles();
  • fs.promises — Promise-based API for async/await usage
  • fs.mkdir() — Create directories (use recursive: true for nested dirs)
  • fs.readdir() — List files and folders in a directory
  • fs.stat() — Get file metadata (size, modified date, etc.)
  • fs.unlink() — Delete a file
  • fs.rename() — Rename or move a file
Notes
  • Use { recursive: true } with fs.mkdir() to create nested directories without errors if they already exist.