🔥 0
0
Lesson 6 of 10 15 min +75 XP

Working with Files

One of Node.js's superpowers is file system access. Let's learn to read, write, and manage files.

The fs Module

const fs = require('fs');

This built-in module gives you full file system access.

Reading Files

Async (recommended):
// read-async.js
const fs = require('fs');

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

console.log('This prints while file is being read...');
Output:
This prints while file is being read...
File contents: Hello, World!
Sync (blocks everything):
// read-sync.js
const fs = require('fs');

try {
  const data = fs.readFileSync('hello.txt', 'utf8');
  console.log('File contents:', data);
} catch (err) {
  console.error('Error:', err.message);
}

console.log('This prints after file is read');
Output:
File contents: Hello, World!
This prints after file is read

Writing Files

Create or overwrite a file:
// write.js
const fs = require('fs');

const content = 'Hello, Node.js!\nThis is line 2.';

fs.writeFile('output.txt', content, 'utf8', (err) => {
  if (err) {
    console.error('Error writing file:', err.message);
    return;
  }
  console.log('File written successfully!');
});
Append to a file:
// append.js
const fs = require('fs');

fs.appendFile('log.txt', 'New log entry\n', (err) => {
  if (err) {
    console.error('Error:', err.message);
    return;
  }
  console.log('Content appended!');
});

Checking if Files Exist

// exists.js
const fs = require('fs');

// Modern way - check access
fs.access('myfile.txt', fs.constants.F_OK, (err) => {
  if (err) {
    console.log('File does not exist');
  } else {
    console.log('File exists');
  }
});

// Sync version
if (fs.existsSync('myfile.txt')) {
  console.log('File exists');
}

Working with Directories

// directories.js
const fs = require('fs');

// Create a directory
fs.mkdir('new-folder', (err) => {
  if (err && err.code !== 'EEXIST') {
    console.error('Error creating directory:', err.message);
    return;
  }
  console.log('Directory created (or already exists)');
});

// Read directory contents
fs.readdir('.', (err, files) => {
  if (err) {
    console.error('Error:', err.message);
    return;
  }
  console.log('Files in current directory:');
  files.forEach(file => console.log(' -', file));
});

// Delete a file
fs.unlink('temp.txt', (err) => {
  if (err) {
    console.error('Error deleting:', err.message);
    return;
  }
  console.log('File deleted');
});

The path Module

Always use path for file paths - it handles OS differences:

// path-demo.js
const path = require('path');

// Join paths safely
const filePath = path.join('users', 'alice', 'documents', 'file.txt');
console.log(filePath);
// Windows: users\alice\documents\file.txt
// Mac/Linux: users/alice/documents/file.txt

// Get parts of a path
const fullPath = '/home/user/projects/app.js';
console.log(path.dirname(fullPath));   // /home/user/projects
console.log(path.basename(fullPath));  // app.js
console.log(path.extname(fullPath));   // .js

// Get absolute path
console.log(path.resolve('file.txt'));
// /Users/you/current-directory/file.txt

// __dirname = directory of current file
console.log(__dirname);
// /Users/you/projects/my-app

// Combine with __dirname for reliable paths
const configPath = path.join(__dirname, 'config', 'settings.json');

Real Example: Simple Logger

// logger.js
const fs = require('fs');
const path = require('path');

const logFile = path.join(__dirname, 'app.log');

function log(message) {
  const timestamp = new Date().toISOString();
  const logLine = `[${timestamp}] ${message}\n`;

  // Print to console
  console.log(logLine.trim());

  // Append to file
  fs.appendFile(logFile, logLine, (err) => {
    if (err) console.error('Failed to write log:', err.message);
  });
}

// Usage
log('Application started');
log('User logged in');
log('Processing request...');
app.log contents:
[2024-01-15T10:30:00.000Z] Application started
[2024-01-15T10:30:01.000Z] User logged in
[2024-01-15T10:30:02.000Z] Processing request...

Try It Yourself

Create a simple note-taking CLI:

// notes.js
const fs = require('fs');
const path = require('path');

const notesFile = path.join(__dirname, 'notes.json');

// Load existing notes
function loadNotes() {
  try {
    const data = fs.readFileSync(notesFile, 'utf8');
    return JSON.parse(data);
  } catch (err) {
    return []; // No notes file yet
  }
}

// Save notes
function saveNotes(notes) {
  fs.writeFileSync(notesFile, JSON.stringify(notes, null, 2));
}

// Get command and arguments
const command = process.argv[2];
const noteText = process.argv.slice(3).join(' ');

const notes = loadNotes();

switch (command) {
  case 'add':
    if (!noteText) {
      console.log('Usage: node notes.js add <note text>');
      break;
    }
    notes.push({ text: noteText, date: new Date().toISOString() });
    saveNotes(notes);
    console.log('Note added!');
    break;

  case 'list':
    if (notes.length === 0) {
      console.log('No notes yet. Add one with: node notes.js add <text>');
    } else {
      console.log('Your notes:');
      notes.forEach((note, i) => {
        console.log(`${i + 1}. ${note.text}`);
      });
    }
    break;

  case 'clear':
    saveNotes([]);
    console.log('All notes cleared!');
    break;

  default:
    console.log('Commands: add, list, clear');
}
Run it:
node notes.js add Buy groceries
node notes.js add Call mom
node notes.js list
node notes.js clear
Output:
Note added!
Note added!
Your notes:
1. Buy groceries
2. Call mom
All notes cleared!

Key Takeaways

  • fs module provides file system access
  • Async methods (readFile, writeFile) are non-blocking
  • Sync methods (readFileSync) block the event loop
  • path module handles OS path differences
  • __dirname gives you the current file's directory
  • Always handle errors - files might not exist

Next, let's learn about async/await for cleaner asynchronous code!

🧠 Quick Quiz

Test your understanding of this lesson.

1

What's the difference between fs.readFile and fs.readFileSync?

2

What does fs.appendFile do?

3

Why use path.join instead of string concatenation for file paths?

The Event Loop