File based operations using NodeJS
Jan 15, 2020- nodejs
- filesystem
- crud
- fs
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.
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.
1const fs = require('fs')
If you're using ES, you can import it like this.
1import 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
orbuffer
. - options - an object containing key-values for configuration for ex.
encoding
The return value for this method is undefined
.
1fs.writeFileSync('./example.txt', 'example 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.
1fs.writeFile('./example.txt', 'example content', error => {2 if (error) console.log(error)3 console.log('The file has been saved!')4})
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.
1const data = fs.readFileSync('./example.txt')2// data - "example content"
Asynchronous Method
1fs.readFile('./example.txt', (error, data) => {2 if (error) console.log(error)3 console.log(data)4})5// data - "example 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.
1fs.writeFileSync('./example.txt', 'example content')
Asynchronous Method
1fs.writeFile('./example.txt', 'example content', error => {2 if (error) console.log(error)3 console.log('The file has been updated!')4})
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.
1fs.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.
1fs.rename('./example.txt', './example1.txt', error => {2 if (error) console.log(error)3 console.log('The file has been renamed!')4})
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.
1fs.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.
1fs.unlink('./example1.txt', error => {2 if (error) console.log(error)3 console.log('The file has been deleted!')4})
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.
1const fileExists = fs.existsSync('./example1.txt')2// 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.
1const create = (filePath, content) => {2 try {3 const fileExists = fs.existsSync(filePath)4 if (fileExists) {5 throw {6 success: false,7 message: 'The file already exist!',8 }9 } else {10 fs.writeFileSync(filePath, content)11 return {12 success: true,13 message: 'The file has been created!',14 }15 }16 } catch (error) {17 return error18 }19}20create('./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
1const read = filePath => {2 try {3 const fileExists = fs.existsSync(filePath)4 if (fileExists) {5 const content = fs.readFileSync(filePath, 'utf8')6 return {7 success: true,8 data: content,9 }10 } else {11 throw {12 success: false,13 message: "The file doesn't exist!",14 }15 }16 } catch (error) {17 return error18 }19}20const 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.
1const update = (filePath, content) => {2 try {3 const fileExists = fs.existsSync(filePath)4 if (fileExists) {5 fs.writeFileSync(filePath, content)6 return {7 success: true,8 message: 'The file has been updated!',9 }10 } else {11 throw {12 success: false,13 message: "The file doesn't exist!",14 }15 }16 } catch (error) {17 return error18 }19}20update('./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.
1const rename = (oldPath, newPath) => {2 try {3 const oldFileExists = fs.existsSync(oldPath)4 const newFileExists = fs.existsSync(newPath)5 if (newFileExists) {6 throw {7 success: false,8 message: "The file you're trying to rename to already exist!",9 }10 }11 if (oldFileExists) {12 fs.renameSync(oldPath, newPath)13 return {14 success: true,15 message: 'The file has been renamed!',16 }17 } else {18 throw {19 success: false,20 message: "The file you're trying to rename doesn't exist!",21 }22 }23 } catch (error) {24 return error25 }26}27rename('./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.
1const unlink = filePath => {2 try {3 const fileExists = fs.existsSync(filePath)4 if (fileExists) {5 fs.unlinkSync(filePath)6 return {7 success: true,8 message: 'The file has been deleted!',9 }10 } else {11 throw {12 success: false,13 message: "The file doesn't exist!",14 }15 }16 } catch (error) {17 return error18 }19}20unlink('./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