If you don't already know, Node.js is a JavaScript runtime environment that is open-source and cross-platform. Its support includes Windows, Linux, Unix, and MacOS. It essentially allows JavaScript to run outside of the browser, contrary to its conventional use. This enables the ability to develop robust web servers, connect to third-party drivers, manage packages and access the file system.
Node.js comes with two types of modules; "built-in" and "external". External modules are those that you need to install using the Node Package Manager (npm). Some examples of external modules are express, multer, and cors. You would typically install any of these using the npm install terminal command and specifying the module e.g. npm install express. On the other hand, built-in modules are modules that come already available in any Node.js script.
There is no need to install these and one such example is the file system module. Some more examples of built-in modules are shown below.
| Module | Description |
|---|---|
buffer |
To handle binary data |
child_process |
To run a child process |
crypto |
To handle OpenSSL cryptographic functions |
dns |
To do DNS lookups and name resolution functions |
os |
Provides information about the operating system |
path |
To handle file paths |
This article covers the following topics:
- Creating a Folder
- Browsing a Folder
- Checking For a File
- Creating/Appending A File
- Showing File Contents
- Renaming a File
- Removing a File
The File System Module
To work with the file system module, you simply need to have Node.js installed on your computer (installation instructions). Next, create a project folder, open a new terminal and run the command npm init. This will start a dialogue in the terminal allowing you to give your preferences such as project name, author, GitHub repository etc. When this dialogue process is complete, a new file with the name "package.json" will be automatically created in the project folder. The contents of this folder should look something like this by default:
{
"name": "node.js-file-system-module",
"version": "1.0.0",
"main": "script.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"description": ""
}
Ready to GO🚀!
Now that the project folder has been properly initialized, we can start accessing the various modules that Node.js has to offer. According to the official documentation, "The node:fs module enables interacting with the file system in a way modeled on standard POSIX functions."
Inside a JavaScript file (with a .js extension 😉), we can include this module by writing const fs = require("fs");. If you go ahead and print this variable to the console, it displays all the methods that are provided (console.log(fs)). As you will see, there are many of them. Let's have a look at some of these methods in detail.
Creating a Folder (mkdir)
The mkdir method asynchronously creates a directory. This function accepts three parameters (path, options, callback).
- The
pathis a string that should point to a relative path within the project folder. - The
optionsargument is an object that looks like this{ recursive: true }. - The
callbackis simply a function that is executed when this function is called.
For example:
fs.mkdir("./my_folder", { recursive: true }, (err) => {
if (err) throw err;
});
The JavaScript code above will create a folder in the root directory called "my_folder" if it does not already exist. Notice how we've used {recursive: true}. Calling fs.mkdir() when path is a directory that exists results in an error only when recursive is false. If recursive is false and the directory exists, an EEXIST error occurs.
You can alternatively import the method directly like this:
const { mkdir } = require('fs');
mkdir("./my_folder", { recursive: true }, (err) => {
if (err) throw err;
});
On Windows, using fs.mkdir() on the root directory even with recursion set to true will result in an error:
mkdir('/', { recursive: true }, (err) => {
// => [Error: EPERM: operation not permitted, mkdir 'C:\']
});
The alternative method for creating a directory synchronously is mkdirSync. This returns undefined, or if recursive is equal to true, the first directory path is created. This is the synchronous version of mkdir.
Browsing a Folder (readdirSync)
As the name suggests, the readdirSync method reads the contents of the directory. This function accepts three parameters which are (path, options, callback). This is an optional argument and is similar to the mkdir function explained above except that the options object has the following properties:
- encoding: A string that specifies the file encoding type
- withFileTypes: Boolean
trueorfalse - recursive: Boolean
trueorfalse
The optional options argument can be a string specifying an encoding, or an object with an encoding property specifying the character encoding to use for the filenames returned. If the encoding is set to 'buffer', the filenames returned will be passed as Buffer objects.
An example to list all the contents of a project folder:
console.log(
fs.readdirSync(
"./",
{ encoding: "utf-8", withFileTypes: true, recursive: true },
(err) => {
if (err) throw err;
}
)
);
If withFileTypes is set to true, the result will contain fs.Dirent objects. This is an object format that shows the name and the path to the file or folder. It looks like this:
Dirent { name: '.vscode', path: './', [Symbol(type)]: 2 }
Setting withFileTypes to false will return an array of strings with the files and folders inside the directory.
Checking For a File (existsSync)
We can test whether or not a given path exists by checking with the file system using the existsSync method. This method returns true if the path exists, and false otherwise. It is essential to note the following:
fs.exists()is deprecated, butfs.existsSync()is not.- The callback parameter to
fs.exists()accepts parameters that are inconsistent with other Node.js callbacks. fs.existsSync()does not use a callback.
Here is an example of this method in action:
function checkPath(path) {
return fs.existsSync(path) ? "File detected" : "File not detected";
}
console.log(checkPath("./my_folder"));
The function above simply looks through the specified directory to see if the path to the string passed as an argument exists and prints a relevant response.
Creating/Appending A File (appendFile)
The appendFile method asynchronously appends data to a file, creating the file if it does not yet exist. It accepts the following parameters appendFile(path, data).
datacan be a string or a Buffer data type.
Here is an example of the appendFile method:
fs.appendFile(
"./my_folder/file.txt",
"The quick big brown fox jumped over the lazy dog.",
"utf8",
(err) => {
if (err) throw error;
console.log("File data inserted");
}
);
You are not only limited to .txt files, you can even create a JavaScript file and write its content as shown below:
fs.appendFile(
"./my_folder/script.js",
"console.log('Hello World!');",
"utf8",
(err) => {
if (err) throw error;
console.log("File data inserted");
}
);
Pretty cool ain't it!?😎
Showing File Contents (readFile)
This method asynchronously reads the entire contents of a file. You can pass three arguments to this method which are path, options, and callback. The callback is passed two arguments (err, data), where data is the contents of the file.
The options object accepts a property called encoding. If no encoding is specified, then the raw buffer is returned. If options is a string, then it specifies the encoding. For example:
// Not specifying the encoding type shows the data in a Buffer format
fs.readFile("./my_folder/script.js", (error, data) => {
if (error) throw error;
console.log(data);
});
// Specifying the encoding type shows the actual contents of the file
fs.readFile("./my_folder/script.js", { encoding: "utf-8" }, (error, data) => {
if (error) throw error;
console.log(data);
});
Using the readFile method on a directory will throw an error that says something like Error: EISDIR: illegal operation on a directory, read. This method can only be used on a file such as a .txt or .js file. However, this is platform-specific. Fun fact🎉: on macOS, Linux, and Windows, an error will be returned. On FreeBSD, a representation of the directory's contents will be returned.
Renaming a File (rename)
This method simply renames a file by asynchronously renaming a file at the old path to the pathname provided as the new path. In the case that the new path already exists, it will be overwritten. If there is a directory at the new path, an error will be raised instead. No arguments other than a possible exception are given to the completion callback.
Here is an example:
fs.rename('./my_folder/script.js', './my_folder/main.js', (err) => {
if (err) throw err;
console.log('Rename complete!');
})
- The first argument is the old path.
- The second argument is the new path.
- The last argument is a callback function which must be included otherwise an error will be thrown.
- The alternative method is
renameSyncwhich returnsundefined.
Removing a File (rm)
If you can create a file, you should be able to remove it right? You can do so by using the rm method. The definition from the docs says "asynchronously removes files and directories (modeled on the standard POSIX rm utility). No arguments other than a possible exception are given to the completion callback.".
This function accepts the arguments of path and callback. Not specifying a callback function will successfully delete the file but will show an error in the console. It is best practice to include a callback function for better error handling. Here is an example:
fs.rm("./my_folder/file.txt", (error) => {
if (error) throw error;
console.log("File deleted successfully!");
});
Attempting to delete a directory using the rm function will thrown an error. This method can only delete a file such as a .txt file.
Deleting a Directory
To delete an entire folder or directory, one ought to use the rmdir method. This method has the exact same function signature as the rm method. Here is an example:
fs.rmdir("./my_folder", (error) => {
if (error) throw error;
console.log("Directory deleted successfully!");
})
Using the fs.rmdir() method on a file (not a directory) results in an ENOENT error on Windows and an ENOTDIR error on POSIX. You can however use the rm method with options { recursive: true, force: true } to get a behavior similar to the rmdir method. Here is an example:
fs.rm("./my_folder", { recursive: true, force: true }, (error) => {
if (error) throw error;
console.log("Directory deleted successfully!");
});
That's A Wrap 🎁
Working with the file system using Node.js is relatively simple. The API is simple to learn and the method names are straight-forward. In this article, I have explored how to use the Node.js file system module to create a folder, browse a folder, check if a file exists, create or append to a file, show file contents, rename a file, and delete a file. Feel free to explore some of the other methods from the Node.js fs module. Hint: just write console.log(fs) to show all the methods from this module. Happy coding until the next one and feel free to contact me.
