Introduction
Understanding the file system in Node.js is essential for developing applications that require reading, writing, and manipulating files. Node.js offers the fs module, which enables synchronous and asynchronous file management. This article explains in detail how to use the fs module in Node.js.
fs module is part of the Node.js API that allows working with the file system. The module can be loaded using the require function:
|
1 |
const fs = require('fs'); |

Reading files
To read the contents of the file, you can use fs.readFile for asynchronous reading or fs.readFileSync for synchronous reading. For example:
Asynchronous reading with callback:
|
1 2 3 4 5 6 7 8 |
const fs = require('fs'); fs.readFile('/path/do/file.txt', 'utf8', (err, data) => { if (err) { console.error(err); return; } console.log(data); }); |
Asynchronous reading with promises:
|
1 2 3 4 5 6 7 8 9 |
const fsPromise = require('fs/promises'); (async () => { try { const data = await fsPromise.readFile('/path/do/file.txt', 'utf8'); console.log(data); } catch (err) { console.error(err); } })(); |
Synchronous reading:
|
1 2 3 4 5 6 |
try { const data = fs.readFileSync('/path/do/file.txt', 'utf8'); console.log(data); } catch (err) { console.error(err); } |
Writing to files
For writing to files, the fs module offers fs.writeFile for asynchronous writing and fs.writeFileSync for synchronous writing. For example:
Asynchronous write with callback:
|
1 2 3 4 5 6 7 8 |
const fs = require('fs'); fs.writeFile('/path/do/file.txt', 'Content to write down', (err) => { if (err) { console.error(err); return; } console.log('The file was written successfully!'); }); |
Asynchronous writing with promises:
|
1 2 3 4 5 6 7 8 9 |
const fsPromise = require('fs/promises'); (async () => { try { await fsPromise.writeFile('/path/do/file.txt', 'Content to write down'); console.log('The file was written successfully!'); } catch (err) { console.error(err); } })(); |
Synchronous writing:
|
1 2 3 4 5 6 |
try { fs.writeFileSync('/path/do/file.txt', 'Content to write down'); console.log('The file was written successfully!'); } catch (err) { console.error(err); } |
Writing open file
If we want to have detailed control over the writing process, including the ability to determine how the writing will take place (creating a new file, appending to the end of an existing one, etc.) and the ability to manage resources then we can apply the following approach:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
const fs = require('fs'); const content = 'This is the text we want to write to the file.'; // Open the file for writing fs.open('/path/do/file.txt', 'w', (err, fd) => { if (err) { return console.error('Error opening file:', err); } console.log(`File open for writing, file descriptor: ${fd}`); // Write to a file using a file descriptor fs.write(fd, content, (err) => { if (err) { console.error('Error writing to file:', err); return; } console.log('Text successfully written.'); // Close the file after writing fs.close(fd, (err) => { if (err) { console.error('Error closing file:', err); return; } console.log('File closed successfully.'); }); }); }); |
Working with directories
fs module also allows creating, reading and deleting directories. For example:
Creating directory with callback:
|
1 2 3 4 5 6 7 8 |
const fs = require('fs'); fs.mkdir('/path/do/novog/direktorijuma', { recursive: true }, (err) => { if (err) { console.error(err); return; } console.log('The directory has been created!'); }); |
Creating directory with promises:
|
1 2 3 4 5 6 7 8 9 |
const fsPromise = require('fs/promises'); (async () => { try { await fsPromise.mkdir('/path/do/novog/direktorijuma', { recursive: true }); console.log('The directory has been created!'); } catch (err) { console.error(err); } })(); |
Reading directory contents with callback:
|
1 2 3 4 5 6 7 8 |
const fs = require('fs'); fs.readdir('/path/do/direktorijuma', (err, files) => { if (err) { console.error(err); return; } console.log(files); }); |
Read directory with promises:
|
1 2 3 4 5 6 7 8 9 |
const fsPromise = require('fs/promises'); (async () => { try { const files = await fsPromise.readdir('/path/do/direktorijuma'); console.log(files); } catch (err) { console.error(err); } })(); |
Delete directory with callback:
|
1 2 3 4 5 6 7 8 |
const fs = require('fs'); fs.rmdir('/path/do/direktorijuma', { recursive: true }, (err) => { if (err) { console.error(err); return; } console.log('The directory has been deleted!'); }); |
Reading open file
If we still want to have fine control over reading, such as specifying the exact position where you want to start reading within the file and how much data you want to read then we can use the following approach
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
const fs = require('fs'); // First open the file to get the file descriptor (fd) fs.open('/path/do/file.txt', 'r', (err, fd) => { if (err) { return console.error('Error opening file:', err); } console.log(`File open, file descriptor: ${fd}`); // Allocate a buffer for reading the contents of the file const bufferSize = 1024; let buffer = Buffer.alloc(bufferSize); // Read the contents of the file fs.read(fd, buffer, 0, bufferSize, null, (err, num) => { if (err) { console.error('Error reading file:', err); return; } console.log(`${num} bytes read.`); // Display the contents of the file console.log(buffer.slice(0, num).toString()); // Close the file after reading fs.close(fd, (err) => { if (err) { console.error('Error closing file:', err); return; } console.log('File closed successfully.'); }); }); }); |
It would be desirable to adjust the buffer to the size of the file, and we will do this using fs.fstat (or fs.stat for asynchronously obtaining information about the file before reading), where we will allocate the buffer exactly according to the file content, which makes reading more efficient, especially for smaller files.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
const fs = require('fs'); // First open the file to get the file descriptor (fd) fs.open('/path/do/file.txt', 'r', (err, fd) => { if (err) { return console.error('Error opening file:', err); } console.log(`File open, file descriptor: ${fd}`); // Get file information to determine its size fs.fstat(fd, (err, stat) => { if (err) { console.error('Error getting file information:', err); return; } // Allocate a buffer of the correct file size const bufferSize = stat.size; let buffer = Buffer.alloc(bufferSize); // Read the contents of the file fs.read(fd, buffer, 0, bufferSize, null, (err, num, readBuffer) => { if (err) { console.error('Error reading file:', err); return; } console.log(`${num} bytes read.`); // Display the contents of the file console.log(readBuffer.toString()); // Close the file after reading fs.close(fd, (err) => { if (err) { console.error('Error closing file:', err); return; } console.log('File closed successfully.'); }); }); }); }); |
Delete directory with promises:
|
1 2 3 4 5 6 7 8 9 |
const fsPromise = require('fs/promises'); (async () => { try { await fsPromise.rm('/path/do/direktorijuma', { recursive: true, force: true }); console.log('The directory has been deleted!'); } catch (err) { console.error(err); } })(); |
Tracking file changes
In Node.js, the watch method from the fs module allows monitoring changes to files or directories. This functionality is useful when you want to automatically respond to changes, such as file updates, without having to manually refresh or restart the application. Here’s how it works:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
const fs = require('fs'); // Tracking changes to a file const filename = '/path/do/file.txt'; fs.watch(filename, (eventType, filename) => { console.log(`Dogodila se promena: ${eventType}`); if (filename) { console.log(`The file that was changed is: ${filename}`); } else { console.log('File not specified'); } }); |
Or with the use of promis:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
const fs = require('fs'); (async () => { try { const watcher = await new Promise((resolve, reject) => { const watcher = fs.watch('/path/do/file.txt', (eventType, filename) => { console.log(`Dogodila se promena: ${eventType}`); if (filename) { console.log(`The file that was changed is: ${filename}`); } else { console.log('File not specified'); } }); watcher.on('error', (error) => reject(error)); resolve(watcher); }); console.log('Monitoring has started.'); } catch (error) { console.error('An error occurred while starting monitoring:', error); } })(); |
When you use fs.watch, it can emit two main types of events: ‘change’ and ‘rename’. The ‘change’ event refers to a change in the contents of a file, while ‘rename’ indicates a change in the directory structure (eg creating a new file, renaming or deleting a file).
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
const fs = require('fs'); (async () => { const watcher = fs.watch('/path/do/fajla_ili_direktorijuma', (eventType, filename) => { if (eventType === 'rename') { console.log(`'rename' event detected for ${filename}.`); // Implement logic specific to the 'rename' event here. // Note: 'rename' can indicate the creation, deletion or renaming of a file. } else if (eventType === 'change') { console.log(`'change' event detected for ${filename}.`); // Here, implement the logic specific to the 'change' event. // This refers to changes within the file. } }); console.log('Monitoring has started.'); // Opcionalno: zaustaviti nadgledanje nakon nekog vremena setTimeout(() => { watcher.close(); console.log('Nadgledanje je zaustavljeno.'); }, 10000); // zaustavlja nadgledanje nakon 10 sekundi })(); |
