File based operations using NodeJS
Tags: node, javascript, filesystem, fs
Published: 2020-01-15 03:58:09

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](https://thepracticaldev.s3.amazonaws.com/i/laiu32llqsl4fik7uto0.jpg "File Cabinet")

Photo by Maksym Kaharlytskyi on [Unsplash](https://unsplash.com/photos/vYdSo_GhHCg "https://unsplash.com/photos/vYdSo_GhHCg")

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](https://nodejs.org/ "https://nodejs.org/") 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.

```js
const fs = require('fs')
```
If you're using ES, you can import it like this.

```js
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`.

```js
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.

```js
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.

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

**Asynchronous Method**
```js
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.

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

**Asynchronous Method**

```js
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.

```js
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.

```js
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.

```js
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.

```js
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.

```js
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.

```js
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

```js
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.

```js
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.

```js
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.

```js
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](https://nodejs.org/api/fs.html "https://nodejs.org/api/fs.html") for the documentation for `fs` module on NodeJs website for reference.

Need to ask a quick question?
Ask away on my twitter [@prvnbist](https://twitter.com/prvnbist "https://twitter.com/prvnbist")

*originally posted on my [blog](https://www.prvnbist.com/blog/file-based-operations-using-nodejs "https://www.prvnbist.com/blog/file-based-operations-using-nodejs")*