Node.js File System Module

The fs module lets you read, write, and manage files on the filesystem. It provides both callback-based and promise-based APIs.

Two styles

Callback style is the original API. You will see it in older code.

fs.promises with async/await is the modern approach. It is cleaner and easier to reason about. Prefer this in new code.

Importing

// CommonJS
const fs = require('fs');
const { readFile, writeFile } = require('fs/promises'); // promise API

// ES modules
import fs from 'node:fs';
import { readFile, writeFile } from 'node:fs/promises';

Reading files

Callback style:

const fs = require('fs');

fs.readFile('./docs/blog.txt', 'utf8', (err, data) => {
  if (err) throw err;
  console.log(data);
});

Async/await (recommended):

import { readFile } from 'node:fs/promises';

const data = await readFile('./docs/blog.txt', 'utf8');
console.log(data);

If you omit the encoding, you get a Buffer instead of a string. Pass 'utf8' to get the text content directly.

Writing files

Callback:

fs.writeFile('./docs/blog.txt', 'Hello there', (err) => {
  if (err) throw err;
  console.log('File written');
});

Async/await:

import { writeFile } from 'node:fs/promises';

await writeFile('./docs/blog.txt', 'Hello there');
console.log('File written');

writeFile() creates the file if it does not exist and overwrites it if it does.

Appending to a file

import { appendFile } from 'node:fs/promises';

await appendFile('./docs/blog.txt', '\nNew line added');

Renaming a file

import { rename } from 'node:fs/promises';

await rename('./docs/blog.txt', './docs/newblog.txt');

Deleting a file

import { unlink } from 'node:fs/promises';

await unlink('./docs/blog.txt');

Checking if a file exists

With async/await:

import { access } from 'node:fs/promises';

try {
  await access('./docs/blog.txt');
  console.log('File exists');
} catch {
  console.log('File does not exist');
}

With the synchronous method (useful in scripts where async is not needed):

import fs from 'node:fs';

if (fs.existsSync('./docs/blog.txt')) {
  console.log('File exists');
}

Working with directories

Create a folder

import { mkdir } from 'node:fs/promises';

await mkdir('./images', { recursive: true }); // recursive: true avoids error if folder already exists

Delete a folder

fs.rmdir() is deprecated. Use fs.rm() instead:

import { rm } from 'node:fs/promises';

await rm('./images', { recursive: true });

Streams

For large files, loading the entire content into memory at once is slow and wasteful. Streams let you process data in chunks as it arrives.

Read stream:

const fs = require('fs');

const readStream = fs.createReadStream('./docs/blog3.txt', { encoding: 'utf8' });

readStream.on('data', (chunk) => {
  console.log('-- New Chunk --');
  console.log(chunk);
});

Writing a stream:

const fs = require('fs');

const readStream = fs.createReadStream('./docs/blog3.txt', { encoding: 'utf8' });
const writeStream = fs.createWriteStream('./docs/blog4.txt');

readStream.on('data', (chunk) => {
  writeStream.write(chunk);
});

Using pipe() to connect a readable stream to a writable stream (shorter version):

const fs = require('fs');

const readStream = fs.createReadStream('./docs/blog3.txt', { encoding: 'utf8' });
const writeStream = fs.createWriteStream('./docs/blog4.txt');

readStream.pipe(writeStream);

For more methods, see the fs documentation .