File based operations using NodeJS

Jan 15, 20204mins read

Ever since NodeJS' inception, we've been able to execute JavaScript code outside of the browser. But NodeJS did a lot more than just that, it opened up a way to write server side code with JavaScript itself and along with it came the ability to manipulate host system's file system.

File Cabinet

Photo by Maksym Kaharlytskyi on Unsplash

NodeJs introduced the fs module that allows you to do synchronous or asynchronous I/O operations and it's available out of the box.

Getting Started

Make sure you've the node installed in your system, if not you can head over to Node's official site and download it from there. Now with that installed we're up and ready to do some file based operations.

To use the fs, we can use the code below. If you're using commonjs use this line of code.

const fs = require('fs')

If you're using ES, you can import it like this.

import fs from 'fs'

Now each of the operations we'll be learning have both synchronous and asynchronous methods. All the synchronous methods have Sync as a suffix. All the asynchronous methods takes a callback as it's last argument which gives us an error as first argument and data as second argument containing the result that some of the operation's return. With that being said and done, let's do some operations.

CRUD operations

Using the fs module, we can implement following operations -

  • Create
  • Read
  • Update
  • Rename
  • Delete

Create File

In order to create a new file, we can use fs.writeFile or fs.writeFileSync.

Synchronous Method

This method takes three arguments:

  • file - path of the file, where it would be stored
  • data - content to store inside the file, can be string or buffer.
  • options - an object containing key-values for configuration for ex. encoding

The return value for this method is undefined.

fs.writeFileSync('./example.txt', 'exampel content')

By default the encoding for data of string type would be utf8 and if different encoding is required, pass it using the third argument named options.

Asynchronous Method

This method takes all the arguments same as the synchronous method except it let's you pass a callback.

fs.writeFile('./example.txt', 'exampel content', (error) => {
if(error) console.log(error);
console.log('The file has been saved!')
})

Read File

Now if we want to read the content of the file example.txt that we just created. We can use either fs.readFile or fs.readFileSync.

Synchronous Method

This method takes just one argument i.e path of the file where it's stored and returns the contents stored in that file. The content could be either of type string or buffer. With buffer type, simply convert it to string using toString() method.

const data = fs.readFileSync('./example.txt')
// data - "exampel content"

Asynchronous Method

fs.readFile('./example.txt', (error, data) => {
if(error) console.log(error);
console.log(data)
})
// data - "exampel content"

Update File

Now that we have access to the content of the file and we want to update it because there's a typo you made or maybe I did which is perfectly normal, you can use the method's fs.writeFile or fs.writeFileSync again to overwrite your data.

Synchronous Method

This method just returns undefined, because incase you're file doesn't exist it'll create a new one using the path itself and store the content in that file.

fs.writeFileSync('./example.txt', 'example content')

Asynchronous Method

fs.writeFile('./example.txt', 'example content', (error) => {
if(error) console.log(error);
console.log('The file has been updated!')
})

Rename File

This method can be used for two purposes i.e for renaming a file/folder or moving a file/folder from one folder to another. The most likely error that it will throw is if the new name that was provided is a folder but incase if it's a file it will be overwritten. It will also throw an error if the folder you're moving the file to does not exist.

Synchronous Method

This method just takes two arguements: oldPath and newPath. Return undefined if the operation was successfull. Throws error if newPath doesn't exist or newPath is a folder.

fs.renameSync('./example.txt', './example1.txt')

Asynchronous Method

This method has similar signature as the synchronous one with an extra callback, giving us an error object that can be logged.

fs.rename('./example.txt', './example1.txt', (error) => {
if(error) console.log(error);
console.log('The file has been renamed!')
})

Delete File

The methods we have for deleting a file are fs.unlink and fs.unlinkSync. The most likely error it could throw is if the file you're trying to delete doesn't exist.

Synchronous Method

This version just takes a path of type string or buffer or a URL. Returns undefined if there are no errors.

fs.unlinkSync('./example1.txt')

Asynchronous Method

This version takes a path and callback as arguments. Callback gets just the error argument that can be used to log the error.

fs.unlink('./example1.txt', (error) => {
if(error) console.log(error);
console.log('The file has been deleted!')
})

Validation

These methods can get the job done but they're not enough because any error thrown in production, if not catched will stop the server. For ex. when you update a file, you would not want to update a wrong file because you passed tire instead of tier considering they both exist for some reason. So what do we do, we bring in validation. Simple checks before performing any operations to validate if a file exists or not.

There's a method that fs module provides for checking if a file/folder exists or not, named existsSync. The asynchronous method for this has been deprecated.

const fileExists = fs.existsSync('./example1.txt')
// fileExists - false

Now we can write our validation for file based operations.

Create File

Let's start by creating a function named create and we'll pass both the filePath and content to it. We'll use try catch to catch all the errors that could possibly be thrown.

const create = (filePath, content) => {
try {
const fileExists = fs.existsSync(filePath);
if (fileExists) {
throw {
success: false,
message: "The file already exist!"
};
} else {
fs.writeFileSync(filePath, content);
return {
success: true,
message: "The file has been created!"
};
}
} catch (error) {
return error;
}
};
create("./example.txt", "Example Content")

Read File

Similarly for reading a file, we can write function called read and pass our filePath to it. Before returning the content

const read = filePath => {
try {
const fileExists = fs.existsSync(filePath);
if (fileExists) {
const content = fs.readFileSync(filePath, 'utf8');
return {
success: true,
data: content
};
} else {
throw {
success: false,
message: "The file doesn't exist!"
};
}
} catch (error) {
return error;
}
};
const content = read("./example.txt")

Update File

Before updating a file, we'll check if it exists or not and throw an error if it does not.

const update = (filePath, content) => {
try {
const fileExists = fs.existsSync(filePath);
if (fileExists) {
fs.writeFileSync(filePath, content);
return {
success: true,
message: "The file has been updated!"
};
} else {
throw {
success: false,
message: "The file doesn't exist!"
};
}
} catch (error) {
return error;
}
};
update('./example.txt', "New Example Content")

Rename File

With renaming a file, we'll have to make sure that both the path's i.e oldPath and newPath exists. In case you're trying to move a file, make sure the folder you're moving the file into also exists.

const rename = (oldPath, newPath) => {
try {
const oldFileExists = fs.existsSync(oldPath);
const newFileExists = fs.existsSync(newPath);
if (newFileExists) {
throw {
success: false,
message: "The file you're trying to rename to already exist!"
};
}
if (oldFileExists) {
fs.renameSync(oldPath, newPath);
return {
success: true,
message: "The file has been renamed!"
};
} else {
throw {
success: false,
message: "The file you're trying to rename doesn't exist!"
};
}
} catch (error) {
return error;
}
};
rename("./example.txt", "./example1.txt")

Delete File

Similarly for deleting a file, check if it exists and if it does then delete it or throw an error.

const unlink = filePath => {
try {
const fileExists = fs.existsSync(filePath);
if (fileExists) {
fs.unlinkSync(filePath);
return {
success: true,
message: "The file has been deleted!"
};
} else {
throw {
success: false,
message: "The file doesn't exist!"
};
}
} catch (error) {
return error;
}
};
unlink("./example1.txt")

Conclusion

These are basic operations you might need when you want manipulate file system. The fs module contains a plethora of functions like these that you can make use of.

Here's the link for the documentation for fs module on NodeJs website for reference.

Need to ask a quick question? Ask away on my twitter @prvnbist