devnotes.

The File System (fs) Module

fs is how Node.js reads, writes, and manages files and directories — one of the modules that made server-side JavaScript genuinely useful from day one.

Three ways to call the same operation

Most fs methods come in three flavors, and knowing which to reach for matters:

// Synchronous — blocks until done, simplest to read
const data = fs.readFileSync('data.txt', 'utf8');
console.log(data);

// Callback-based — the traditional non-blocking style
fs.readFile('data.txt', 'utf8', (err, data) => {
  if (err) return console.error(err);
  console.log(data);
});

// Promise-based — works cleanly with async/await
const fsPromises = require('fs/promises');

async function readIt() {
  const data = await fsPromises.readFile('data.txt', 'utf8');
  console.log(data);
}

For anything running while your server actively handles requests, favor the callback or Promise versions — as covered in the earlier blocking/non-blocking page, the synchronous version freezes the whole process until it finishes.

Writing and appending

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

await fsPromises.writeFile('log.txt', 'Server started\n'); // overwrites the file entirely
await fsPromises.appendFile('log.txt', 'User logged in\n'); // adds to the end instead

Checking whether a file exists before touching it

const fs = require('fs');

try {
  await fsPromises.access('config.json', fs.constants.F_OK);
  console.log('File exists');
} catch {
  console.log('File does not exist');
}

Working with directories

await fsPromises.mkdir('uploads', { recursive: true }); // recursive: also creates parent folders as needed

const files = await fsPromises.readdir('uploads');
console.log(files); // array of filenames in that directory

const stats = await fsPromises.stat('uploads/photo.jpg');
console.log(stats.size, stats.isFile(), stats.isDirectory());

Deleting

await fsPromises.unlink('temp-file.txt');           // deletes one file
await fsPromises.rm('old-uploads', { recursive: true }); // deletes a folder and everything in it

A realistic small example — saving a JSON record

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

async function saveUser(user) {
  const existing = JSON.parse(await fsPromises.readFile('users.json', 'utf8').catch(() => '[]'));
  existing.push(user);
  await fsPromises.writeFile('users.json', JSON.stringify(existing, null, 2));
}

saveUser({ id: 1, name: 'Nusrat' });

The .catch(() => '[]') handles the case where users.json doesn't exist yet, falling back to an empty array rather than crashing.

One important boundary worth knowing

For genuinely large files (video, large datasets), reading the whole thing into memory at once with readFile can be wasteful or even crash the process if the file is bigger than available memory. That's exactly the problem streams solve, covered on a later page — fs.createReadStream() and fs.createWriteStream() process a file in manageable pieces instead of loading it all at once.

The File System (fs) Module | DevNotes