Work With Files in Node.js
Node.js·26 min read·Jan 1, 2025
In Node.js, most scripts and applications require access to the files stored on the disk of the machine they run on.
Common use cases include, reading from configuration files, processing and converting data files (e.g., JSON, CSV), generating execution logs, creating automated backups, and more.
The fs module
The fs module (short for "file system") provides several APIs for interacting with the filesystem in a way modeled on standard POSIX functions.
Each API of the fs module has a callback, synchronous, and promise form, where:
- The callback and synchronous APIs are accessible using the
node:fsimport. - The promise APIs are accessible using the
node:fs/promisesimport.
The callback APIs have the following signature:
fs.api(...params, callback)
The synchronous APIs have the following signature:
fs.apiSync(...params)
The promise APIs have the following signature:
fsPromises.api(...params)
Note: For the sake of simplicity and brevity, we'll only cover the synchronous version of the APIs and their most commonly used options.
Get information about a file
To retrieve information about a file, you can use the fs.statSync() method:
const stats = fs.statSync(path);
Where:
statsis afs.Statsobject.pathis the path to the target file.
This method returns a fs.Stats object that has built-in methods for validating the file type, such as isFile() to check if the file is a regular file, and properties, such as size to check its size in bytes.
Note: It is similar to the Unix
statscommand.
Example
Let's consider this script that retrieves information about the script file itself:
const fs = require('node:fs');
try {
const stats = fs.statSync(__filename);
console.log('File type: ' + (stats.isFile() ? 'regular file' : 'other'));
console.log('File owner: ' + stats.uid);
console.log('File size: ' + stats.size);
console.log('File creation: ' + stats.birthtime);
} catch(error) {
console.error(error);
}
💡 Tip: In Node.js, the special
__filenamevariable contains the absolute path to the current module.
When executed, it will:
-
Declare a
statsvariable and initialize it with thefs.Statsobject returned by the call to thefs.statSync()method. -
Output information about the file, derived from the
statsobject.
Which will produce this output:
File type: regular file
File owner: 501
File size: 278
File creation: Tue Sep 03 2024 13:42:48 GMT+0200 (Central European Summer Time)
Read the content of a file
To read and store the entire content of a file into a variable, you can use the fs.readFileSync() static method:
const content = fs.readFileSync(path, { encoding? });
Where:
contentis either a string or buffer.pathis the path to the target file.encodingis an optional string used to specify the character encoding (e.g.,'utf8').
Note: This method will return a string if the
encodingis specified, or aBufferobject otherwise.
Example
Let's consider this CSV file named leads.csv located in the current directory:
first_name,last_name,email_address
John,Doe,johndoe@email.com
Alice,Kepler,a.kepler@email.com
Marvin,Ianopoulos,mrvinpls33@email.com
Let's consider this script that extracts email addresses from a CSV file:
const fs = require('node:fs');
try {
const leads = fs.readFileSync('leads.csv', { encoding: 'utf8' });
const emails = leads.split('\n').reduce((emails, row) => {
const emailRegex = /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/;
const columns = row.split(',');
if (emailRegex.test(columns[2])) {
emails.push(columns[2]);
}
return emails;
}, []);
console.log(emails);
} catch(error) {
console.error(error);
}
When executed, it will:
- Declare a variable named
leadsand initialize it with the UTF-8-encoded string returned by thefs.readFileSync()method. - Split the string contained in the
leadsvariable into an array of strings using the newline character\nas delimiter. - Split each substring into an array of strings using the comma character
,as delimiter. - Check if the 3rd substring is a valid email address and add it to the
emailarray. - Output the list of emails.
Which will produce this output:
[ 'johndoe@email.com', 'a.kepler@email.com', 'mrvinpls33@email.com' ]
Write data into a file
To write data into a file, you can use the writeFileSync() static method:
fs.writeFileSync(path, data, { encoding?, mode? });
Where:
pathis the path to the target file.datais a string or buffer to write to the file.encodingis an optional string used to specify the character encoding. Defaults to'utf8'.modeis an optional octal number used to specify the file permissions. Defaults to0o666.
Alternatively, to append data to a file, you can use the fs.appendFileSync() static method:
fs.appendFileSync(path, data, { encoding?, mode? });
Notes:
- The
fs.writeFileSync()method will completely overwrite the file.- Both the
fs.writeFileSync()andfs.appendFileSync()methods will automatically create the file if it doesn't exist.
Example
Let's consider this script that generates random hero names:
const fs = require('node:fs');
const adjectives = ['Mighty', 'Shadow', 'Galactic', 'Spectral', 'Crimson'];
const nouns = ['Warrior', 'Guardian', 'Ranger', 'Knight', 'Sentinel'];
function generateHeroName() {
const randomAdjective = adjectives[Math.floor(Math.random() * adjectives.length)];
const randomNoun = nouns[Math.floor(Math.random() * nouns.length)];
return `${randomAdjective} ${randomNoun}`;
}
function generateHeroNames(number = 1) {
let names = [];
if (number <= 0) {
return [generateHeroName()];
}
while (names.length < 5) {
let name = generateHeroName();
if (!names.includes(name)) {
names.push(name);
}
}
return names;
}
const heroNames = generateHeroNames(5).join('\n') + '\n';
try {
fs.writeFileSync('heroes.txt', heroNames);
} catch(error) {
console.error(error);
}
When executed, it will:
- Declare a variable named
adjectivesand initialize it with an array of strings. - Declare a variable named
nounsand initialize it with an array of strings. - Define a function named
generateHeroName()that randomly selects an entry from theadjectivesandnounsarrays, and returns a concatenated string of both values representing a hero name. - Define a function named
generateHeroNames()that invokes thegenerateHeroName()function, adds the hero name to thenamesarray if it doesn't exist, and returns the array. - Declare a variable named
heroNamesand initialize it with the concatenated list of hero names generated by thegenerateHeroNames()function. - Attempt to write the content of the
heroNamesvariable into the file namedheroes.txt.
Which will produce this output:
$ cat heroes.txt
Shadow Knight
Crimson Guardian
Mighty Ranger
Spectral Knight
Shadow Sentinel
Copy a file
To create a copy of a regular file, you can use the fs.copyFileSync() static method:
fs.copyFileSync(source, destination);
Where:
sourceis the path to the original file to copy.destinationis the path to the destination file to copy it as.
Note: This function is similar to the Unix
cpcommand, which means that if the file already exists, it will be automatically overwritten.
Example
Let's consider this script that creates a backup of data files:
const path = require('node:path');
const fs = require('node:fs');
function getCurrentDate() {
const date = new Date();
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}${month}${day}`;
}
const backupDirectory = './backups';
const dataDirectory = './data';
const files = ['customers.csv', 'orders.csv'];
try {
for (const file of files) {
const source = path.join(dataDirectory, file);
const destination = path.join(backupDirectory, `${getCurrentDate()}_${file}`);
console.log(`Copying '${source}' -> '${destination}'`);
fs.copyFileSync(source, destination);
console.log('Done.');
}
} catch(error) {
console.error(error);
}
When executed, it will:
- Define a function named
getCurrentDate()that returns the current date in theYYYYMMDDformat using theDateobject. - Declare a variable named
backupDirectoryand initialize it with the path to the directory the files will be copied to. - Declare a variable named
dataDirectoryand initialize it with the path to the directory the files will be copied from. - Declare a variable named
filesand initialize it with the list of filenames to copy. - Iterate on each file using a
for...ofloop. - Declare a variable named
sourceand initialize it with the full path to the original file. - Declare a variable named
destinationand initialize it with the full path to the copy file, prefixed with the current date. - Copy the source into the destination using the
fs.copyFileSync()method.
Which will produce this output:
Copying 'data/customers.csv' -> 'backups/20241114_customers.csv'
Done.
Copying 'data/orders.csv' -> 'backups/20241114_orders.csv'
Done.
Remove a file
To remove a file, you can use the fs.rmSync() method:
fs.rmSync(path, { force? });
Where:
pathis the path to the file you want to remove.forceis an optional boolean used to ignore exceptions whenpathdoesn't exist.
Note: This function is similar to the Unix
rmcommand.
Example
Let's consider this script that removes files older than a certain amount of days:
const path = require('node:path');
const fs = require('node:fs');
function isOlderThan(creationMs, days) {
const currentMs = Date.now();
const daysMs = days * 24 * 60 * 60 * 1000;
return currentMs - creationMs > daysMs;
}
const directory = './logs';
const files = ['usage.log', 'error.log'];
try {
for (let file of files) {
const filepath = path.join(directory, file);
const stats = fs.statSync(filepath);
if (stats.isFile() && isOlderThan(stats.birthtimeMs, 30)) {
console.log(`Deleting file: '${filepath}'...`);
fs.rmSync(path);
console.log('Done');
} else {
console.log(`Skipping file: '${filepath}'`);
}
}
} catch(error) {
console.error(error);
}
When executed, it will:
- Define a function named
isOlderThan()that takes as arguments a file creation time in milliseconds and a number of days, and returns whether the file is older than the number of days. - Declare a variable named
directoryand initialize it with the path string of the base directory to remove files from. - Declare a variable named
filesand initialize it with an array of filenames to check and remove. - Iterate on each file using a
for...ofloop. - Declare a variable named
filepathand initialize it with the full path to the current file. - Declare a variable named
statsand initialize it with thefs.Statsobject of this file. - Attempt to remove the file if it is a regular file and it is older than 30 days.
Which will produce this output:
Skipping file: 'data/usage.log'
Deleting file: 'logs/error'...
Done
🗒️ Summary
Here's a summary of what you've learned in this lesson:
- The
node:fscore module provides APIs for interacting with the filesystem. - The
fs.statSync()method retrieves information about a file. - The
fs.readFileSync()method returns the content of a file as a string orBuffer. - The
fs.writeFileSync()method writes a string orBufferinto a file. - The
fs.appendFileSync()method writes a string orBufferat the end of a file. - The
fs.copyFileSync()method copies a file into another file. - The
fs.rmSync()method removes a file.