README
ApickFS
Modern File Storage library for Nodejs v12.14.0 and above.
ApickFS is a free and open source File System library based on Node's fs.promises() that helps developers ditch callbacks and outdated depencencies.
🎓 Learning ApickFS
Full documentation for ApickFS lives on the website.
🧩 Why ApickFS:
- Based on cutting edge fs.promises
- Async Awiat based code
- No outdated dependencies
- Utilising latest features of Nodejs 12.14.0 Long Term released
- Minimal base library
- Easy to extend
- Easy to read source code
- Automatic JSON parsing
- Auto Subdirectory creation
🎰 Available Methods:
Please click on the below method names to go to the documentation of that function
File opeations
- fileExists()
- fileExistsSync()
- readByLineNumbers()
- getLinesCount()
- writeLines()
- deleteFile()
- openFile()
- writeFile()
Directory operations
- directoryExists()
- directoryExistsSync()
- getDirectoryEntries()
- deleteDirectory()
- openDirectory()
- writeDirectory()
Low level functions
- fileOrFolderExists()
- readStorage()
- removeStorage()
- removeStorageRecursively()
- storageHandler()
- writeStorage()
Important Notes
- ApickFS requires Node 12.14.0 LTS
- Feel free to create a github issue if required.
- It will be great if every Pull request is accompanied by a github issue.
Install and Include
Install the ApickFS file Storage.
npm i apickfs
Make sure that you are using Nodejs v12.14.0
or above.
Check your node version now:
node -v
In case you need to manage different verions of Node in your system, we strongy reccoment NVM
Including required methods in your file
const { writeFile, deleteFile, deleteDirectory, getDirectoryEntries } = require('apickfs');
You can just include the name of the method that you need. Incase you prefer to include the whole library you can do that too.
const apickFileStorage = require('apickfs');
In case you decide to include the whole library, you can use any apickFS method using a . (dot)
apickFileStorage.writeFile();
apickFileStorage.deleteFile();
apickFileStorage.deleteDirectory();
apickFileStorage.getDirectoryEntries();
Methods for file operations
fileExists()
Arguments: directoryPath , fileName
Result: a promise that resolves into true
if file exists and false
if it doesnot exist.
Example:
{ fileExists } = require('apickfs');
Inside an Async Function :
const {
const my_file_exists = await fileExists(__dirname, 'my-file.json');
console.log(my_file_exists);
Inside a normal Function :
fileExists(__dirname, 'my-file.json')
.then(d => {
console.log(d);
})
.catch(e => {
console.log(e);
});
Why differnt arguments for filename and directory path?
At several occassions we need to loop over several files in a particular directory. This approach make it a bit flexible in those use cases. And also we have tried to keep the API consistent.
fileExistsSync()
Arguments
directoryPath, fileName
Result
true
if file exist and false
if it doesnot exist.
Examples
const { fileExistsSync } = require('apickfs');
const file_exists = fileExistsSync(__dirname, 'my-file.txt');
console.log(file_exists);
readByLineNumbers()
Reads the particular line number from a file. In case it's valid JSON file, its automatically parsed to an Object or an Array. We can provide an array of line numbers to read multiple lines at a time.
Arguments
const { readByLineNumbers } = require('apickfs');
Arguments
- directoryPath required, path to directory
- fileName required, file name
- lineNumbers required, the line number or the array of line numbers you want to read.
Result
apickFS result object
succss: ture/false
message: A descriptive message.
data; An array of lines read from the file.
Examples
** Async Function **
const myData = await readByLineNumbers(__dirname, 'my-file.txt', 10);
console.log(myData);
const myData = await readByLineNumbers(__dirname, 'my-file.txt', [1, 3]);
console.log(myData);
** Normal Function **
readByLineNumbers(__dirname, 'some-file-name.txt', 10)
.then(d => {
console.log(d);
})
.catch(e => {
console.log(e);
});
** success response **
{
success: true,
message: 'SUCCESS: 1 lines successfully read from /Users/vivek/practice-apps/apickdb/my-file.txt.',
data: [ { hello: 'world' } ]
}
{
success: true,
message: 'SUCCESS: 2 lines successfully read from /Users/vivek/practice-apps/apickdb/my-file.txt.',
data: [ { hello: 'world' }, { hello: 'world' } ]
}
** fail response **
{
success: false,
message: 'Line number index 10 does not exist in /Users/vivek/practice-apps/apickdb/some-file-name.txt.'
}
getLinesCount()
Returns a promise that resolves into the total number of lines in the provided file.
Import
const path = require('path');
const { getLinesCount } = require('apickfs');
Arguments
- directoryName: required, string, directory path
- fileName: required, string, filename
Result
A promise that resolves into an integer of total number of lines in the provided file.
Examples
** Async Function **
const linesCount = await getLinesCount(path.join(__dirname, 'bigdata'), 'big.txt');
console.log(linesCount);
** Normal Function **
getLinesCount(path.join(__dirname, 'some-folder'), 'my-file.txt')
.then(d => {
console.log(d);
})
.catch(e => {
console.log(e);
});
** Example success response **
{
success: true,
message: 'SUCCESS: /Users/vivek/practice-apps/apickdb/bigdata/big.txt has total 163845 lines.',
data: 163845
}
** Example fail response **
{
success: false,
message: 'Error: /Users/vivek/practice-apps/apickdb/bigdataxxx doesnot exist.'
}
writeLines()
Write a line or several lines to the end of the file.
Import
const { writeLines } = require('apickfs');
Arguments
- directoryPath: required, string, path to the directory.
- fileName: required, string, filename with extension.
- LinesofData: line of data or an array of lines of data.
Result
apickFS result object
succss: ture/false
message: A descriptive message.
Examples
** Async Function **
Example 1.
let writestatus = await writeLines(path.join(__dirname, 'some-folder'), 'my-file.txt', [
'Hello Javascript',
{ Hello: 'node' }
]);
console.log(writestatus);
Example 2.
let writestatus = await writeLines(path.join(__dirname), 'my-file.txt', 'Hello Javascript');
console.log(writestatus);
Example 3.
let writestatus = await writeLines(path.join(__dirname), 'my-file.txt', { hello: 'hello' });
console.log(writestatus);
** Normal Function **
writeLines(path.join(__dirname), 'my-file.txt', { hello: 'hello' })
.then(d => {
console.log(d);
})
.catch(e => {
console.log(e);
});
** Example success response **
{
success: true,
message: 'SUCCESS: 2 lines successfully written to my-file.txt.'
}
** Example fail response **
{
success: false,
message: 'Error: /Users/vivek/practice-apps/brad-cli/apickdb/my-file.txtxx doesnot exist.'
}
deleteFile()
Deletes the file if it exists.
Import
const { deleteFile } = require('apickfs');
Arguments
- directoryPath required, string, path of the directory.
- fileName required, string, name of the file.
Result
apickFS result object
succss: ture/false
message: A descriptive message.
Examples
** Async Function **
let delstat = await deleteFile((__dirname, 'some-folder'), 'my-file.txt');
console.log(delstat);
** Normal Function **
deleteFile(__dirname, 'todelete.txt')
.then(d => {
console.log(d);
})
.catch(e => {
console.log(e);
});
** Example success response **
{
success: true,
message: 'FILE deleted: /Users/vivek/practice-apps/brad-cli/apickdb/todelete.txt'
}
** Example fail response **
{ success: false, message: 'Directory does not exist: some-folder' }
openFile()
Open file just returns an apickResultObject with success: true
if the file exists. It created on if
it doesnot exists. It never overwrites an existing file.
On the other hand writeFile()
creates the file if it doesnot exist, but overwrites it in case it exists.
Unlike node's native open method, apickFS.openFile() doesnot return a fileHandle. We have special methods to like writeLine
to open and add data to files. In most of the cases, you will be using openFile() most of the times.
Import
const { openFile } = require('apickfs');
Arguments
- directoryPath: required, string, path to the directory of the file.
- fileName: required, string, name of the file.
Result
apickFS result object
succss: ture/false
message: A descriptive message.
Examples
** Async Function **
let openstat = await openFile(path.join(__dirname), 'my-file1.txt');
console.log(openstat);
** Normal Function **
openFile(path.join(__dirname), 'my-file1.txt')
.then(d => {
console.log(d);
})
.catch(e => {
console.log(e);
});
** Example success response **
{
success: true,
message: 'SUCCESS: /Users/vivek/practice-apps/brad-cli/apickdb/my-file.txt exists.'
}
{
success: true,
message: 'SUCCESS: /Users/vivek/practice-apps/brad-cli/apickdb/my-file1.txt created.'
}
** Example fail response **
{
success: false,
message: 'ERROR: Both directoryPath and filename are required parameters.'
}
writeFile()
Write file creates a new file if it doesnot exist. It takes in optional data as well. Incase the file already exists, it complelely overwrites it with the new data. In case new data is not provided, the existing file is replaced with an empty new file.
writeFile()
acts differently from openFile()
when the file already exists.
Open file never overwrites data
whereas writeFile file overwrites the content of the file.
Just like most of the apickFS functions, it too parses valid Objects and Arrays to JSON by default.
Import
const { writeFile } = require('apickfs');
Arguments
- directoryPath: required, string, path to the files directory.
- fileName: required, string, filename
- fileData: optional, String/Object/Array, the data for file.
Result
apickFS result object
succss: ture/false
message: A descriptive message.
Examples
** Async Function **
let writestat = await writeFile(path.join(__dirname), 'to-overwrite.txt', { hello: 'world' });
console.log(writestat);
** Normal Function **
writeFile(path.join(__dirname), 'to-overwrite.txt', { hello: 'world' })
.then(d => {
console.log(d);
})
.catch(e => {
console.log(e);
});
** Example success response **
{
success: true,
message: 'Success: /Users/vivek/practice-apps/brad-cli/apickdb/to-overwrite.txt successfully created/overwritten.'
}
Methods for directory ops
directoryExists()
Returns a promise that resolves to true if the directory exists else it resolves to false.
Import
const { directoryExists } = require('apickfs');
Arguments
- directoryPath: required, String, path to the directory.
Result
true
/false
Examples
** Async Function **
let dirstat = await directoryExists(path.join(__dirname));
console.log(dirstat);
** Normal Function **
directoryExists(path.join(__dirname))
.then(d => {
console.log(d);
})
.catch(e => {
console.log(e);
});
** Example success response **
true
** Example fail response **
false
directoryExistsSync()
Returns true
if the directory exists and false
in case it doesnot exist.
Import
const { directoryExistsSync } = require('apickfs');
Arguments
- directoryPath: required, String, Path to the directory
Result
true
/false
Examples
** Normal Function **
let dirStat = directoryExistsSync(path.join(__dirname, 'data'));
console.log(dirStat);
** Example success response **
true
** Example fail response **
false
getDirectoryEntries()
Recursively lists all the directories and files inside the given directory. Provides us output in several formats depending on the options provided.
Import
const { getDirectoryEntries } = require('apickfs');
Arguments
- directoryPath: required, String, path to the directory.
- options: optional, Object
- maxLevel: integer, default:0(Unlimited)
- listFiles: boolean, default:
true
- listDirectories: boolean, default:
true
- getFullPath:boolean, default:
true
- ignoreEmptyExtension: boolean, default:
false
- mustHaveExtensions: Array of String, default:
[]
- mustNotHaveExtensions: Array of String, default:
[]
- mustStartWith: Array of String, default:
[]
- mustNotStartWith: Array of String, default:
[]
- mustInclude: Array of String, default:
[]
- mustNotInclude: Array of String, default:
[]
- extraDetails: boolean, default:
false
Result
Array of string
or Array of Object
depnding on the options provided.
Examples
Example to list just files
** Example Code **
let direntries = await getDirectoryEntries(__dirname, { listDirectories: false, getFullPath: false });
console.log(direntries);
** Example Response **
[
'.DS_Store',
'index.js',
'index2.js',
'my-file.txt',
'to-overwrite.txt',
'.data.db',
'nestedarray.json',
'nesteobj.json',
'colors.json',
'app.js',
'post.json',
'fileOrFolderExists.js',
'big 4.txt',
'big 2.txt',
'big 3.txt',
'big.txt',
'my-file1.txt'
];
Example to get full path
** Example Code **
let direntries = await getDirectoryEntries(__dirname, { listDirectories: false });
console.log(direntries);
** Example Response **
[
'/Users/vivek/practice-apps/brad-cli/apickdb/.DS_Store',
'/Users/vivek/practice-apps/brad-cli/apickdb/index.js',
'/Users/vivek/practice-apps/brad-cli/apickdb/index2.js',
'/Users/vivek/practice-apps/brad-cli/apickdb/my-file.txt',
'/Users/vivek/practice-apps/brad-cli/apickdb/to-overwrite.txt',
'/Users/vivek/practice-apps/brad-cli/apickdb/.db-kittens/kittens/.data.db',
'/Users/vivek/practice-apps/brad-cli/apickdb/data/nestedarray.json',
'/Users/vivek/practice-apps/brad-cli/apickdb/data/nesteobj.json',
'/Users/vivek/practice-apps/brad-cli/apickdb/data/colors.json',
'/Users/vivek/practice-apps/brad-cli/apickdb/data/app.js',
'/Users/vivek/practice-apps/brad-cli/apickdb/data/post.json',
'/Users/vivek/practice-apps/brad-cli/apickdb/data/fileOrFolderExists.js',
'/Users/vivek/practice-apps/brad-cli/apickdb/bigdata/big 4.txt',
'/Users/vivek/practice-apps/brad-cli/apickdb/bigdata/big 2.txt',
'/Users/vivek/practice-apps/brad-cli/apickdb/bigdata/big 3.txt',
'/Users/vivek/practice-apps/brad-cli/apickdb/bigdata/big.txt',
'/Users/vivek/practice-apps/brad-cli/apickdb/my-file1.txt'
];
Example to get just Json files
** Example Code **
let direntries = await getDirectoryEntries(__dirname, { listDirectories: false, mustHaveExtensions: ['json'] });
console.log(direntries);
** Example Response **
[
'/Users/vivek/practice-apps/brad-cli/apickdb/data/nestedarray.json',
'/Users/vivek/practice-apps/brad-cli/apickdb/data/nesteobj.json',
'/Users/vivek/practice-apps/brad-cli/apickdb/data/colors.json',
'/Users/vivek/practice-apps/brad-cli/apickdb/data/post.json'
];
Get some Extra details
** Example Code **
(async () => {
let direntries = await getDirectoryEntries(__dirname, {
listDirectories: false,
mustHaveExtensions: ['json'],
extraDetails: true
});
console.log(direntries);
** Example Response **
[
{
type: 'file',
level: 3,
fullPath: '/Users/vivek/practice-apps/brad-cli/apickdb/data/nestedarray.json',
extension: 'json',
parent: '/Users/vivek/practice-apps/brad-cli/apickdb/data'
},
{
type: 'file',
level: 3,
fullPath: '/Users/vivek/practice-apps/brad-cli/apickdb/data/nesteobj.json',
extension: 'json',
parent: '/Users/vivek/practice-apps/brad-cli/apickdb/data'
},
{
type: 'file',
level: 4,
fullPath: '/Users/vivek/practice-apps/brad-cli/apickdb/data/colors.json',
extension: 'json',
parent: '/Users/vivek/practice-apps/brad-cli/apickdb/data'
},
{
type: 'file',
level: 4,
fullPath: '/Users/vivek/practice-apps/brad-cli/apickdb/data/post.json',
extension: 'json',
parent: '/Users/vivek/practice-apps/brad-cli/apickdb/data'
}
];
Get whose name starts with '.' or 'big'
** Example Code **
let direntries = await getDirectoryEntries(__dirname, {
listDirectories: false,
getFullPath: false,
mustStartWith: ['.', 'big']
});
console.log(direntries);
** Example Response **
['.DS_Store', '.data.db', 'big 4.txt', 'big 2.txt', 'big 3.txt', 'big.txt'];
Example to get just directories
** Example Code **
let direntries = await getDirectoryEntries(__dirname, {
listFiles: false,
getFullPath: false
});
console.log(direntries);
** Example Response **
['t2', '.db-kittens', 'kittens', 'data', '.data', 'bigdata'];
deleteDirectory()
Recursively deletes a directory and its contents. In case you want to stop it from deleting if the directory has some content the pass false
as second argument.
Import
const { deleteDirectory } = require('apickfs');
Arguments
- directoryPath: required, string
- recursive: optional, boolean, default=
true
Result
apickFS result object
succss: ture/false
message: A descriptive message.
Examples
** Example Code **
let dirstat = await deleteDirectory(path.join(__dirname, 't2'));
console.log(dirstat);
** Example Response **
{
success: true,
message: 'Directory deleted: /Users/vivek/practice-apps/brad-cli/apickdb/t2 recursively.'
}
openDirectory()
If the directory exists, it returns apickFsResultObject with success set to true. else creates one. openDirectory
never overwrites existing directory. Unlike node's native open
it doesnot return any handles.
This method is useful where you want to create a directory by never want to overwrite it.
Import
const { openDirectory } = require('apickfs');
Arguments
- directoryPath, required, String, path of the directory to open
Result
apickFS result object
succss: ture/false
message: A descriptive message.
Examples
** Example Code **
let dirstat = await openDirectory(path.join(__dirname, 't2'));
console.log(dirstat);
** Example Response **
{
success: true,
message: 'SUCCESS: /Users/vivek/practice-apps/brad-cli/apickdb/t2 already exists.'
}
writeDirectory()
If directory exist, it overwrites it with an empty new directory. If directory doesnot exist, it creates one.
writeDirectory
is uselful when you want to make sure that you are starting from an empty foleder. For rest of the cases openDirectory
is the right choice.
Import
const { writeDirectory } = require('apickfs');
Arguments
- directoryPath: required, String, path to the directory
Result
apickFS result object
succss: ture/false
message: A descriptive message.
Examples
** Example Code **
let dirstat = await writeDirectory(path.join(__dirname, 't2'));
console.log(dirstat);
** Example Response **
{
success: true,
message: 'SUCCESS: /Users/vivek/practice-apps/brad-cli/apickdb/t2 updated/overwritten.'
}
Low level Methods
Not to be used most of the times. We have specific mehtods of files and directories.
Parameter Combinations | result | Description |
---|---|---|
writeStorage(directoryPath) |
Success, Messge | Creates directory |
writeStorage(directoryPath , fieName) |
Success, Messge | Creates or Truncates file |
writeStorage(directoryPath , fieName, data) |
Success, Messge | Creates file. Adds or Overwrites file content with data. |
removeStorage(directoryPath) |
Success, Messge | Removes directory |
removeStorage(directoryPath , fieName) |
Success, Messge | removes file |
removeStorage(directoryPath,null,true) |
Success, Messge | Removes directory recursively |
removeStorageRecursively(directoryPath) |
Success, Messge | Removes directory recursively |
fileOrFolderExists(<directory/file> path) |
true/false | Checks if a directory or a file exists |
storageHander(<directory/file> path) |
true/false , file/directory | Checks if a directory or a file exists and its type. |
fileOrFolderExists()
check if a file
or a directory
exists:
apickFileStorage
.fileOrFolderExists(path.join(__dirname, 'boo'))
.then(d => {
console.log(d);
})
.catch(e => {
console.log(e);
});
It returns true
or false
false
check if a file
or a directory
exists:
apickFileStorage
.fileOrFolderExists(path.join(__dirname, 'boo'))
.then(d => {
console.log(d);
})
.catch(e => {
console.log(e);
});
It takes just one argument and returns true
or false
false
Example with a file:
apickFileStorage
.fileOrFolderExists(path.join(__dirname, 'data', 'text.txt'))
.then(d => {
console.log(d);
})
.catch(e => {
console.log(e);
});
true
readStorage()
readStorage can be used with three different set of arguments. When just one argument is passed it is treated as a directory path. the second argument is used as file name. and the third file is used as data for file.
second and third arguments are optional.
removeStorage()
Removing data.json
in the foo
directory:
apickFileStorage
.removeStorage(path.join(__dirname, 'foo'), 'data.json')
.then(d => {
console.log(d);
})
.catch(e => {
console.log(e);
});
Removing the boo
directory (if it is empty):
apickFileStorage
.removeStorage(path.join(__dirname, 'boo'))
.then(d => {
console.log(d);
})
.catch(e => {
console.log(e);
});
There are two way to remove boo
if it's not empty (recursively)
Removing the boo
directory (if it is empty):
Way 1 : provide a directory path
, keep filename null
and pass true
for allowing recursive deleting
apickFileStorage
.removeStorage(path.join(__dirname, 'boo'), null, true)
.then(d => {
console.log(d);
})
.catch(e => {
console.log(e);
});
Way 2 :
apickFileStorage
.removeStorageRecursively(path.join(__dirname, 'boo'))
.then(d => {
console.log(d);
})
.catch(e => {
console.log(e);
});
removeStorageRecursively()
removeStorageRecursively
is same as removeStorage with third argument for recursive
option set to true.
apickFileStorage
.removeStorageRecursively(path.join(__dirname, 'boo'))
.then(d => {
console.log(d);
})
.catch(e => {
console.log(e);
});
storageHandler()
check if a file
or a directory
exists and also get its type if it exists.
apickFileStorage
.storageHander(path.join(__dirname, 'data', 'text.txt'))
.then(d => {
console.log(d);
})
.catch(e => {
console.log(e);
});
{ exists: true, type: 'file' }
Example with a folder :
apickFileStorage
.storageHander(path.join(__dirname, 'data'))
.then(d => {
console.log(d);
})
.catch(e => {
console.log(e);
});
{ exists: true, type: 'directory' }
writeStorage()
writeStorage()
- the selection of this name has a purpose. Its made up of two words = Write
and Storage
.
Write stands for both creating and editing whereas Store stands for both files and directories.
Creating a directory called foo
in our current directory :
Inside an async function :
(async () => {
try {
status = await apickFileStorage.writeStorage(path.join(__dirname, 'foo'));
console.log(status);
} catch (err) {
console.log(err);
}
})();
Outside an async function :
apickFileStorage
.writeStorage(path.join(__dirname, 'fboo'))
.then(d => {
console.log(d);
})
.catch(e => {
console.log(e);
});
Run any one of the above and if all is good you must get a response object similar to it :
{
success: true,
message: 'SUCCESS: /Users/vivek/practice-apps/temp/fspromises/foo created.'
}
You have a sucess
value to check if the opration was success and you have a message
that can be logged or shown to the user.
You may use any one of async/await or then/catch approach.
Again, for most of the use cases, we have specific methods for files and directories.